aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-07-21 15:51:30 +0200
committerChocobozzz <me@florianbigard.com>2021-07-21 15:51:30 +0200
commita24bd1ed41b43790bab6ba789580bb4e85f07d85 (patch)
treea54b0f6c921ba83a6e909cd0ced325b2d4b8863c
parent5f26f13b3c16ac5ae0a3b0a7142d84a9528cf565 (diff)
parentc63830f15403ac4e750829f27d8bbbdc9a59282c (diff)
downloadPeerTube-a24bd1ed41b43790bab6ba789580bb4e85f07d85.tar.gz
PeerTube-a24bd1ed41b43790bab6ba789580bb4e85f07d85.tar.zst
PeerTube-a24bd1ed41b43790bab6ba789580bb4e85f07d85.zip
Merge branch 'next' into develop
-rw-r--r--.github/workflows/test.yml5
-rw-r--r--client/src/app/+about/about-instance/contact-admin-modal.component.ts3
-rw-r--r--client/src/app/+accounts/accounts.component.ts3
-rw-r--r--client/src/app/+admin/admin.component.ts4
-rw-r--r--client/src/app/+admin/admin.module.ts3
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.html4
-rw-r--r--client/src/app/+admin/follows/following-list/follow-modal.component.html42
-rw-r--r--client/src/app/+admin/follows/following-list/follow-modal.component.scss3
-rw-r--r--client/src/app/+admin/follows/following-list/follow-modal.component.ts69
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.html21
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts22
-rw-r--r--client/src/app/+admin/follows/following-list/index.ts1
-rw-r--r--client/src/app/+admin/follows/follows.routes.ts4
-rw-r--r--client/src/app/+login/login.component.html4
-rw-r--r--client/src/app/+login/login.component.ts4
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts7
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts4
-rw-r--r--client/src/app/+page-not-found/page-not-found.component.ts3
-rw-r--r--client/src/app/+video-channels/video-channels.component.ts2
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts3
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts11
-rw-r--r--client/src/app/core/auth/auth.service.ts3
-rw-r--r--client/src/app/core/rest/rest-extractor.service.ts3
-rw-r--r--client/src/app/helpers/utils.ts2
-rw-r--r--client/src/app/shared/form-validators/batch-domains-validators.ts60
-rw-r--r--client/src/app/shared/form-validators/host-validators.ts105
-rw-r--r--client/src/app/shared/form-validators/host.ts8
-rw-r--r--client/src/app/shared/form-validators/index.ts3
-rw-r--r--client/src/app/shared/shared-instance/instance-follow.service.ts13
-rw-r--r--client/src/app/shared/shared-main/auth/auth-interceptor.service.ts8
-rw-r--r--client/src/app/shared/shared-moderation/batch-domains-modal.component.html14
-rw-r--r--client/src/app/shared/shared-moderation/batch-domains-modal.component.ts8
-rw-r--r--client/src/sass/application.scss8
-rw-r--r--client/src/standalone/videos/embed.ts2
-rw-r--r--scripts/benchmark.ts50
-rwxr-xr-xscripts/generate-code-contributors.ts4
-rw-r--r--server.ts2
-rw-r--r--server/controllers/activitypub/inbox.ts2
-rw-r--r--server/controllers/api/abuse.ts2
-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.ts10
-rw-r--r--server/controllers/api/plugins.ts2
-rw-r--r--server/controllers/api/search/search-video-channels.ts3
-rw-r--r--server/controllers/api/search/search-video-playlists.ts5
-rw-r--r--server/controllers/api/search/search-videos.ts3
-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.ts12
-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/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.ts2
-rw-r--r--server/controllers/api/users/my-video-playlists.ts2
-rw-r--r--server/controllers/api/video-channel.ts2
-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.ts2
-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.ts2
-rw-r--r--server/controllers/api/videos/watching.ts2
-rw-r--r--server/controllers/client.ts2
-rw-r--r--server/controllers/download.ts3
-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.ts2
-rw-r--r--server/helpers/custom-validators/follows.ts20
-rw-r--r--server/helpers/custom-validators/servers.ts1
-rw-r--r--server/helpers/custom-validators/video-ownership.ts2
-rw-r--r--server/helpers/database-utils.ts10
-rw-r--r--server/helpers/express-utils.ts2
-rw-r--r--server/helpers/youtube-dl.ts2
-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/videos/refresh.ts2
-rw-r--r--server/lib/client-html.ts2
-rw-r--r--server/lib/job-queue/handlers/activitypub-cleaner.ts2
-rw-r--r--server/lib/moderation.ts2
-rw-r--r--server/middlewares/activitypub.ts2
-rw-r--r--server/middlewares/auth.ts2
-rw-r--r--server/middlewares/cache.ts4
-rw-r--r--server/middlewares/error.ts2
-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/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.ts2
-rw-r--r--server/middlewares/validators/videos/video-blacklist.ts2
-rw-r--r--server/middlewares/validators/videos/video-channels.ts2
-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.ts2
-rw-r--r--server/middlewares/validators/webfinger.ts2
-rw-r--r--server/models/actor/actor-follow.ts26
-rw-r--r--server/models/video/sql/videos-id-list-query-builder.ts6
-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.ts152
-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.ts132
-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.ts77
-rw-r--r--server/tests/api/live/live-permanent.ts99
-rw-r--r--server/tests/api/live/live-save-replay.ts112
-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.ts275
-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.ts39
-rw-r--r--server/tests/api/notifications/comments-notifications.ts173
-rw-r--r--server/tests/api/notifications/moderation-notifications.ts218
-rw-r--r--server/tests/api/notifications/notifications-api.ts130
-rw-r--r--server/tests/api/notifications/user-notifications.ts115
-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.ts201
-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.ts43
-rw-r--r--server/tests/api/search/search-index.ts206
-rw-r--r--server/tests/api/search/search-playlists.ts83
-rw-r--r--server/tests/api/search/search-videos.ts331
-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.ts111
-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.ts132
-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.ts29
-rw-r--r--server/tests/api/videos/multiple-servers.ts459
-rw-r--r--server/tests/api/videos/resumable-upload.ts48
-rw-r--r--server/tests/api/videos/single-server.ts286
-rw-r--r--server/tests/api/videos/video-captions.ts93
-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.ts101
-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.ts341
-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.ts64
-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.ts87
-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.ts79
-rw-r--r--server/tests/client.ts196
-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.ts239
-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/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.ts400
-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.ts138
-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.ts121
-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/typings/express/index.d.ts3
-rw-r--r--shared/core-utils/miscs/index.ts2
-rw-r--r--shared/extra-utils/bulk/bulk-command.ts20
-rw-r--r--shared/extra-utils/bulk/bulk.ts25
-rw-r--r--shared/extra-utils/bulk/index.ts1
-rw-r--r--shared/extra-utils/cli/cli-command.ts23
-rw-r--r--shared/extra-utils/cli/cli.ts24
-rw-r--r--shared/extra-utils/cli/index.ts1
-rw-r--r--shared/extra-utils/custom-pages/custom-pages-command.ts33
-rw-r--r--shared/extra-utils/custom-pages/custom-pages.ts31
-rw-r--r--shared/extra-utils/custom-pages/index.ts1
-rw-r--r--shared/extra-utils/feeds/feeds-command.ts44
-rw-r--r--shared/extra-utils/feeds/feeds.ts33
-rw-r--r--shared/extra-utils/feeds/index.ts1
-rw-r--r--shared/extra-utils/index.ts66
-rw-r--r--shared/extra-utils/logs/index.ts1
-rw-r--r--shared/extra-utils/logs/logs-command.ts43
-rw-r--r--shared/extra-utils/logs/logs.ts32
-rw-r--r--shared/extra-utils/miscs/checks.ts46
-rw-r--r--shared/extra-utils/miscs/generate.ts61
-rw-r--r--shared/extra-utils/miscs/index.ts5
-rw-r--r--shared/extra-utils/miscs/miscs.ts170
-rw-r--r--shared/extra-utils/miscs/sql-command.ts142
-rw-r--r--shared/extra-utils/miscs/sql.ts161
-rw-r--r--shared/extra-utils/miscs/stubs.ts14
-rw-r--r--shared/extra-utils/miscs/tests.ts94
-rw-r--r--shared/extra-utils/miscs/webtorrent.ts30
-rw-r--r--shared/extra-utils/mock-servers/index.ts4
-rw-r--r--shared/extra-utils/mock-servers/mock-email.ts (renamed from shared/extra-utils/miscs/email.ts)4
-rw-r--r--shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts (renamed from shared/extra-utils/mock-servers/joinpeertube-versions.ts)0
-rw-r--r--shared/extra-utils/mock-servers/mock-plugin-blocklist.ts (renamed from shared/extra-utils/plugins/mock-blocklist.ts)0
-rw-r--r--shared/extra-utils/moderation/abuses-command.ts228
-rw-r--r--shared/extra-utils/moderation/abuses.ts244
-rw-r--r--shared/extra-utils/moderation/index.ts1
-rw-r--r--shared/extra-utils/overviews/index.ts1
-rw-r--r--shared/extra-utils/overviews/overviews-command.ts23
-rw-r--r--shared/extra-utils/overviews/overviews.ts34
-rw-r--r--shared/extra-utils/requests/activitypub.ts2
-rw-r--r--shared/extra-utils/requests/check-api-params.ts19
-rw-r--r--shared/extra-utils/requests/index.ts3
-rw-r--r--shared/extra-utils/requests/requests.ts238
-rw-r--r--shared/extra-utils/search/index.ts1
-rw-r--r--shared/extra-utils/search/search-command.ts98
-rw-r--r--shared/extra-utils/search/video-channels.ts36
-rw-r--r--shared/extra-utils/search/video-playlists.ts36
-rw-r--r--shared/extra-utils/search/videos.ts64
-rw-r--r--shared/extra-utils/server/activitypub.ts15
-rw-r--r--shared/extra-utils/server/clients.ts20
-rw-r--r--shared/extra-utils/server/config-command.ts263
-rw-r--r--shared/extra-utils/server/config.ts260
-rw-r--r--shared/extra-utils/server/contact-form-command.ts31
-rw-r--r--shared/extra-utils/server/contact-form.ts31
-rw-r--r--shared/extra-utils/server/debug-command.ts33
-rw-r--r--shared/extra-utils/server/debug.ts33
-rw-r--r--shared/extra-utils/server/directories.ts34
-rw-r--r--shared/extra-utils/server/follows-command.ts141
-rw-r--r--shared/extra-utils/server/follows.ts133
-rw-r--r--shared/extra-utils/server/index.ts15
-rw-r--r--shared/extra-utils/server/jobs-command.ts36
-rw-r--r--shared/extra-utils/server/jobs.ts82
-rw-r--r--shared/extra-utils/server/plugins-command.ts256
-rw-r--r--shared/extra-utils/server/plugins.ts299
-rw-r--r--shared/extra-utils/server/redundancy-command.ts80
-rw-r--r--shared/extra-utils/server/redundancy.ts88
-rw-r--r--shared/extra-utils/server/server.ts378
-rw-r--r--shared/extra-utils/server/servers-command.ts81
-rw-r--r--shared/extra-utils/server/servers.ts375
-rw-r--r--shared/extra-utils/server/stats-command.ts25
-rw-r--r--shared/extra-utils/server/stats.ts23
-rw-r--r--shared/extra-utils/shared/abstract-command.ts199
-rw-r--r--shared/extra-utils/shared/index.ts1
-rw-r--r--shared/extra-utils/socket/index.ts1
-rw-r--r--shared/extra-utils/socket/socket-io-command.ts15
-rw-r--r--shared/extra-utils/socket/socket-io.ts18
-rw-r--r--shared/extra-utils/users/accounts-command.ts56
-rw-r--r--shared/extra-utils/users/accounts.ts87
-rw-r--r--shared/extra-utils/users/actors.ts73
-rw-r--r--shared/extra-utils/users/blocklist-command.ts139
-rw-r--r--shared/extra-utils/users/blocklist.ts238
-rw-r--r--shared/extra-utils/users/index.ts9
-rw-r--r--shared/extra-utils/users/login-command.ts132
-rw-r--r--shared/extra-utils/users/login.ts124
-rw-r--r--shared/extra-utils/users/notifications-command.ts86
-rw-r--r--shared/extra-utils/users/notifications.ts (renamed from shared/extra-utils/users/user-notifications.ts)167
-rw-r--r--shared/extra-utils/users/subscriptions-command.ts99
-rw-r--r--shared/extra-utils/users/user-subscriptions.ts93
-rw-r--r--shared/extra-utils/users/users-command.ts414
-rw-r--r--shared/extra-utils/users/users.ts415
-rw-r--r--shared/extra-utils/videos/blacklist-command.ts76
-rw-r--r--shared/extra-utils/videos/captions-command.ts65
-rw-r--r--shared/extra-utils/videos/captions.ts17
-rw-r--r--shared/extra-utils/videos/change-ownership-command.ts68
-rw-r--r--shared/extra-utils/videos/channels-command.ts156
-rw-r--r--shared/extra-utils/videos/channels.ts18
-rw-r--r--shared/extra-utils/videos/comments-command.ts152
-rw-r--r--shared/extra-utils/videos/history-command.ts58
-rw-r--r--shared/extra-utils/videos/imports-command.ts47
-rw-r--r--shared/extra-utils/videos/index.ts19
-rw-r--r--shared/extra-utils/videos/live-command.ts154
-rw-r--r--shared/extra-utils/videos/live.ts147
-rw-r--r--shared/extra-utils/videos/playlists-command.ts279
-rw-r--r--shared/extra-utils/videos/playlists.ts25
-rw-r--r--shared/extra-utils/videos/services-command.ts29
-rw-r--r--shared/extra-utils/videos/services.ts24
-rw-r--r--shared/extra-utils/videos/streaming-playlists-command.ts44
-rw-r--r--shared/extra-utils/videos/streaming-playlists.ts75
-rw-r--r--shared/extra-utils/videos/video-blacklist.ts79
-rw-r--r--shared/extra-utils/videos/video-captions.ts72
-rw-r--r--shared/extra-utils/videos/video-change-ownership.ts72
-rw-r--r--shared/extra-utils/videos/video-channels.ts192
-rw-r--r--shared/extra-utils/videos/video-comments.ts138
-rw-r--r--shared/extra-utils/videos/video-history.ts49
-rw-r--r--shared/extra-utils/videos/video-imports.ts90
-rw-r--r--shared/extra-utils/videos/video-playlists.ts320
-rw-r--r--shared/extra-utils/videos/video-streaming-playlists.ts82
-rw-r--r--shared/extra-utils/videos/videos-command.ts598
-rw-r--r--shared/extra-utils/videos/videos.ts761
-rw-r--r--shared/models/http/http-error-codes.ts (renamed from shared/core-utils/miscs/http-error-codes.ts)0
-rw-r--r--shared/models/http/http-methods.ts (renamed from shared/core-utils/miscs/http-methods.ts)0
-rw-r--r--shared/models/http/index.ts2
-rw-r--r--shared/models/index.ts1
-rw-r--r--shared/models/search/videos-common-query.model.ts2
-rw-r--r--shared/models/server/debug.model.ts1
-rw-r--r--shared/models/server/index.ts1
-rw-r--r--shared/models/server/peertube-problem-document.model.ts2
-rw-r--r--shared/models/server/server-follow-create.model.ts4
-rw-r--r--shared/models/users/index.ts1
-rw-r--r--shared/models/users/user-create-result.model.ts7
-rw-r--r--shared/models/videos/channel/index.ts1
-rw-r--r--shared/models/videos/channel/video-channel-create-result.model.ts3
-rw-r--r--shared/models/videos/comment/index.ts1
-rw-r--r--shared/models/videos/comment/video-comment-create.model.ts3
-rw-r--r--shared/models/videos/comment/video-comment.model.ts7
-rw-r--r--shared/models/videos/playlist/index.ts1
-rw-r--r--shared/models/videos/playlist/video-playlist-element-create-result.model.ts3
-rw-r--r--shared/models/videos/video-update.model.ts1
-rw-r--r--support/doc/api/openapi.yaml18
-rw-r--r--tsconfig.json1
420 files changed, 14624 insertions, 16594 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 46b243244..c5bbd9e2c 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -2,11 +2,6 @@ name: Test Suite
2 2
3on: 3on:
4 push: 4 push:
5 branches:
6 - develop
7 - master
8 - ci
9 - next
10 pull_request: 5 pull_request:
11 types: [synchronize, opened] 6 types: [synchronize, opened]
12 schedule: 7 schedule:
diff --git a/client/src/app/+about/about-instance/contact-admin-modal.component.ts b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
index a528faa20..37e9feacb 100644
--- a/client/src/app/+about/about-instance/contact-admin-modal.component.ts
+++ b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
@@ -11,8 +11,7 @@ import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
11import { InstanceService } from '@app/shared/shared-instance' 11import { InstanceService } from '@app/shared/shared-instance'
12import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 12import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
13import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 13import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
14import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 14import { HTMLServerConfig, HttpStatusCode } from '@shared/models'
15import { HTMLServerConfig } from '@shared/models'
16 15
17type Prefill = { 16type Prefill = {
18 subject?: string 17 subject?: string
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts
index c69b04a01..5b59f3cd0 100644
--- a/client/src/app/+accounts/accounts.component.ts
+++ b/client/src/app/+accounts/accounts.component.ts
@@ -13,8 +13,7 @@ import {
13 VideoService 13 VideoService
14} from '@app/shared/shared-main' 14} from '@app/shared/shared-main'
15import { AccountReportComponent } from '@app/shared/shared-moderation' 15import { AccountReportComponent } from '@app/shared/shared-moderation'
16import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 16import { HttpStatusCode, User, UserRight } from '@shared/models'
17import { User, UserRight } from '@shared/models'
18import { AccountSearchComponent } from './account-search/account-search.component' 17import { AccountSearchComponent } from './account-search/account-search.component'
19 18
20@Component({ 19@Component({
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts
index dd92ed2ca..4b6fab6ed 100644
--- a/client/src/app/+admin/admin.component.ts
+++ b/client/src/app/+admin/admin.component.ts
@@ -26,12 +26,12 @@ export class AdminComponent implements OnInit {
26 label: $localize`Federation`, 26 label: $localize`Federation`,
27 children: [ 27 children: [
28 { 28 {
29 label: $localize`Instances you follow`, 29 label: $localize`Following`,
30 routerLink: '/admin/follows/following-list', 30 routerLink: '/admin/follows/following-list',
31 iconName: 'following' 31 iconName: 'following'
32 }, 32 },
33 { 33 {
34 label: $localize`Instances following you`, 34 label: $localize`Followers`,
35 routerLink: '/admin/follows/followers-list', 35 routerLink: '/admin/follows/followers-list',
36 iconName: 'follower' 36 iconName: 'follower'
37 }, 37 },
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index a7fe20b07..1ea7b9784 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -25,7 +25,7 @@ import {
25 EditVODTranscodingComponent 25 EditVODTranscodingComponent
26} from './config' 26} from './config'
27import { ConfigService } from './config/shared/config.service' 27import { ConfigService } from './config/shared/config.service'
28import { FollowersListComponent, FollowsComponent, VideoRedundanciesListComponent } from './follows' 28import { FollowersListComponent, FollowModalComponent, FollowsComponent, VideoRedundanciesListComponent } from './follows'
29import { FollowingListComponent } from './follows/following-list/following-list.component' 29import { FollowingListComponent } from './follows/following-list/following-list.component'
30import { RedundancyCheckboxComponent } from './follows/shared/redundancy-checkbox.component' 30import { RedundancyCheckboxComponent } from './follows/shared/redundancy-checkbox.component'
31import { VideoRedundancyInformationComponent } from './follows/video-redundancies-list/video-redundancy-information.component' 31import { VideoRedundancyInformationComponent } from './follows/video-redundancies-list/video-redundancy-information.component'
@@ -68,6 +68,7 @@ import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersCom
68 FollowsComponent, 68 FollowsComponent,
69 FollowersListComponent, 69 FollowersListComponent,
70 FollowingListComponent, 70 FollowingListComponent,
71 FollowModalComponent,
71 RedundancyCheckboxComponent, 72 RedundancyCheckboxComponent,
72 VideoRedundanciesListComponent, 73 VideoRedundanciesListComponent,
73 VideoRedundancyInformationComponent, 74 VideoRedundancyInformationComponent,
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.html b/client/src/app/+admin/follows/followers-list/followers-list.component.html
index c2e9a4df6..08459634d 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.html
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.html
@@ -1,6 +1,6 @@
1<h1> 1<h1>
2 <my-global-icon iconName="follower" aria-hidden="true"></my-global-icon> 2 <my-global-icon iconName="follower" aria-hidden="true"></my-global-icon>
3 <ng-container i18n>Instances following you</ng-container> 3 <ng-container i18n>Followers of your instance</ng-container>
4</h1> 4</h1>
5 5
6<p-table 6<p-table
@@ -21,7 +21,7 @@
21 <ng-template pTemplate="header"> 21 <ng-template pTemplate="header">
22 <tr> 22 <tr>
23 <th style="width: 150px;" i18n>Actions</th> 23 <th style="width: 150px;" i18n>Actions</th>
24 <th i18n>Follower handle</th> 24 <th i18n>Follower</th>
25 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th> 25 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
26 <th style="width: 100px;" i18n pSortableColumn="score">Score <p-sortIcon field="score"></p-sortIcon></th> 26 <th style="width: 100px;" i18n pSortableColumn="score">Score <p-sortIcon field="score"></p-sortIcon></th>
27 <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 27 <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
diff --git a/client/src/app/+admin/follows/following-list/follow-modal.component.html b/client/src/app/+admin/follows/following-list/follow-modal.component.html
new file mode 100644
index 000000000..d0761b718
--- /dev/null
+++ b/client/src/app/+admin/follows/following-list/follow-modal.component.html
@@ -0,0 +1,42 @@
1<ng-template #modal>
2 <div class="modal-header">
3 <h4 i18n class="modal-title">Follow</h4>
4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
6 </div>
7
8 <div class="modal-body">
9 <form novalidate [formGroup]="form" (ngSubmit)="submit()">
10 <div class="form-group">
11 <label i18n for="hostsOrHandles">1 host (without "http://"), account handle or channel handle per line</label>
12
13 <textarea
14 [placeholder]="placeholder" formControlName="hostsOrHandles" type="text" id="hostsOrHandles" name="hostsOrHandles"
15 class="form-control" [ngClass]="{ 'input-error': formErrors['hostsOrHandles'] }" ngbAutofocus
16 ></textarea>
17
18 <div *ngIf="formErrors.hostsOrHandles" class="form-error">
19 {{ formErrors.hostsOrHandles }}
20
21 <div *ngIf="form.controls['hostsOrHandles'].errors.validHostsOrHandles">
22 {{ form.controls['hostsOrHandles'].errors.validHostsOrHandles.value }}
23 </div>
24 </div>
25 </div>
26
27 <div i18n *ngIf="httpEnabled() === false" class="alert alert-warning">
28 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
29 </div>
30
31 <div class="form-group inputs">
32 <input
33 type="button" role="button" i18n-value value="Cancel" class="peertube-button grey-button"
34 (click)="hide()" (key.enter)="hide()"
35 >
36
37 <input type="submit" i18n-value value="Follow" class="peertube-button orange-button" [disabled]="!form.valid" />
38 </div>
39 </form>
40 </div>
41
42</ng-template>
diff --git a/client/src/app/+admin/follows/following-list/follow-modal.component.scss b/client/src/app/+admin/follows/following-list/follow-modal.component.scss
new file mode 100644
index 000000000..9621a566f
--- /dev/null
+++ b/client/src/app/+admin/follows/following-list/follow-modal.component.scss
@@ -0,0 +1,3 @@
1textarea {
2 height: 200px;
3}
diff --git a/client/src/app/+admin/follows/following-list/follow-modal.component.ts b/client/src/app/+admin/follows/following-list/follow-modal.component.ts
new file mode 100644
index 000000000..dc6909200
--- /dev/null
+++ b/client/src/app/+admin/follows/following-list/follow-modal.component.ts
@@ -0,0 +1,69 @@
1import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
2import { Notifier } from '@app/core'
3import { splitAndGetNotEmpty, UNIQUE_HOSTS_OR_HANDLE_VALIDATOR } from '@app/shared/form-validators/host-validators'
4import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
5import { InstanceFollowService } from '@app/shared/shared-instance'
6import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
7import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
8
9@Component({
10 selector: 'my-follow-modal',
11 templateUrl: './follow-modal.component.html',
12 styleUrls: [ './follow-modal.component.scss' ]
13})
14export class FollowModalComponent extends FormReactive implements OnInit {
15 @ViewChild('modal', { static: true }) modal: NgbModal
16
17 @Output() newFollow = new EventEmitter<void>()
18
19 placeholder = 'example.com\nchocobozzz@example.com\nchocobozzz_channel@example.com'
20
21 private openedModal: NgbModalRef
22
23 constructor (
24 protected formValidatorService: FormValidatorService,
25 private modalService: NgbModal,
26 private followService: InstanceFollowService,
27 private notifier: Notifier
28 ) {
29 super()
30 }
31
32 ngOnInit () {
33 this.buildForm({
34 hostsOrHandles: UNIQUE_HOSTS_OR_HANDLE_VALIDATOR
35 })
36 }
37
38 openModal () {
39 this.openedModal = this.modalService.open(this.modal, { centered: true })
40 }
41
42 hide () {
43 this.openedModal.close()
44 }
45
46 submit () {
47 this.addFollowing()
48
49 this.form.reset()
50 this.hide()
51 }
52
53 httpEnabled () {
54 return window.location.protocol === 'https:'
55 }
56
57 private async addFollowing () {
58 const hostsOrHandles = splitAndGetNotEmpty(this.form.value['hostsOrHandles'])
59
60 this.followService.follow(hostsOrHandles).subscribe(
61 () => {
62 this.notifier.success($localize`Follow request(s) sent!`)
63 this.newFollow.emit()
64 },
65
66 err => this.notifier.error(err.message)
67 )
68 }
69}
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.html b/client/src/app/+admin/follows/following-list/following-list.component.html
index e7c0c9088..75b0efca8 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.html
+++ b/client/src/app/+admin/follows/following-list/following-list.component.html
@@ -1,6 +1,6 @@
1<h1> 1<h1>
2 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon> 2 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
3 <ng-container i18n>Instances you follow</ng-container> 3 <ng-container i18n>Your instance subscriptions</ng-container>
4</h1> 4</h1>
5 5
6<p-table 6<p-table
@@ -13,9 +13,9 @@
13 <ng-template pTemplate="caption"> 13 <ng-template pTemplate="caption">
14 <div class="caption"> 14 <div class="caption">
15 <div class="left-buttons"> 15 <div class="left-buttons">
16 <a class="follow-button" (click)="addDomainsToFollow()" (key.enter)="addDomainsToFollow()"> 16 <a class="follow-button" (click)="openFollowModal()" (key.enter)="openFollowModal()">
17 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon> 17 <my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
18 <ng-container i18n>Follow instances</ng-container> 18 <ng-container i18n>Follow</ng-container>
19 </a> 19 </a>
20 </div> 20 </div>
21 21
@@ -28,7 +28,7 @@
28 <ng-template pTemplate="header"> 28 <ng-template pTemplate="header">
29 <tr> 29 <tr>
30 <th style="width: 150px;" i18n>Action</th> 30 <th style="width: 150px;" i18n>Action</th>
31 <th i18n>Host</th> 31 <th i18n>Following</th>
32 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th> 32 <th style="width: 100px;" i18n pSortableColumn="state">State <p-sortIcon field="state"></p-sortIcon></th>
33 <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th> 33 <th style="width: 150px;" i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
34 <th style="width: 160px;" i18n pSortableColumn="redundancyAllowed">Redundancy allowed <p-sortIcon field="redundancyAllowed"></p-sortIcon></th> 34 <th style="width: 160px;" i18n pSortableColumn="redundancyAllowed">Redundancy allowed <p-sortIcon field="redundancyAllowed"></p-sortIcon></th>
@@ -41,8 +41,8 @@
41 <my-delete-button label="Unfollow" i18n-label (click)="removeFollowing(follow)"></my-delete-button> 41 <my-delete-button label="Unfollow" i18n-label (click)="removeFollowing(follow)"></my-delete-button>
42 </td> 42 </td>
43 <td> 43 <td>
44 <a [href]="'https://' + follow.following.host" i18n-title title="Open instance in a new tab" target="_blank" rel="noopener noreferrer"> 44 <a [href]="follow.following.url" i18n-title title="Open instance in a new tab" target="_blank" rel="noopener noreferrer">
45 {{ follow.following.host }} 45 {{ follow.following.name + '@' + follow.following.host }}
46 <span class="glyphicon glyphicon-new-window"></span> 46 <span class="glyphicon glyphicon-new-window"></span>
47 </a> 47 </a>
48 </td> 48 </td>
@@ -57,6 +57,7 @@
57 <td>{{ follow.createdAt | date: 'short' }}</td> 57 <td>{{ follow.createdAt | date: 'short' }}</td>
58 <td> 58 <td>
59 <my-redundancy-checkbox 59 <my-redundancy-checkbox
60 *ngIf="isInstanceFollowing(follow)"
60 [host]="follow.following.host" [redundancyAllowed]="follow.following.hostRedundancyAllowed" 61 [host]="follow.following.host" [redundancyAllowed]="follow.following.hostRedundancyAllowed"
61 ></my-redundancy-checkbox> 62 ></my-redundancy-checkbox>
62 </td> 63 </td>
@@ -75,10 +76,4 @@
75 </ng-template> 76 </ng-template>
76</p-table> 77</p-table>
77 78
78<my-batch-domains-modal #batchDomainsModal i18n-action action="Follow domains" (domains)="addFollowing($event)"> 79<my-follow-modal #followModal></my-follow-modal>
79 <ng-container ngProjectAs="warning">
80 <div i18n *ngIf="httpEnabled() === false" class="alert alert-warning">
81 It seems that you are not on a HTTPS server. Your webserver needs to have TLS activated in order to follow servers.
82 </div>
83 </ng-container>
84</my-batch-domains-modal>
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index b63fe08c0..ba62dfa23 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -4,13 +4,14 @@ import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
4import { InstanceFollowService } from '@app/shared/shared-instance' 4import { InstanceFollowService } from '@app/shared/shared-instance'
5import { BatchDomainsModalComponent } from '@app/shared/shared-moderation' 5import { BatchDomainsModalComponent } from '@app/shared/shared-moderation'
6import { ActorFollow } from '@shared/models' 6import { ActorFollow } from '@shared/models'
7import { FollowModalComponent } from './follow-modal.component'
7 8
8@Component({ 9@Component({
9 templateUrl: './following-list.component.html', 10 templateUrl: './following-list.component.html',
10 styleUrls: [ '../follows.component.scss', './following-list.component.scss' ] 11 styleUrls: [ '../follows.component.scss', './following-list.component.scss' ]
11}) 12})
12export class FollowingListComponent extends RestTable implements OnInit { 13export class FollowingListComponent extends RestTable implements OnInit {
13 @ViewChild('batchDomainsModal') batchDomainsModal: BatchDomainsModalComponent 14 @ViewChild('followModal') followModal: FollowModalComponent
14 15
15 following: ActorFollow[] = [] 16 following: ActorFollow[] = []
16 totalRecords = 0 17 totalRecords = 0
@@ -33,23 +34,12 @@ export class FollowingListComponent extends RestTable implements OnInit {
33 return 'FollowingListComponent' 34 return 'FollowingListComponent'
34 } 35 }
35 36
36 addDomainsToFollow () { 37 openFollowModal () {
37 this.batchDomainsModal.openModal() 38 this.followModal.openModal()
38 } 39 }
39 40
40 httpEnabled () { 41 isInstanceFollowing (follow: ActorFollow) {
41 return window.location.protocol === 'https:' 42 return follow.following.name === 'peertube'
42 }
43
44 async addFollowing (hosts: string[]) {
45 this.followService.follow(hosts).subscribe(
46 () => {
47 this.notifier.success($localize`Follow request(s) sent!`)
48 this.reloadData()
49 },
50
51 err => this.notifier.error(err.message)
52 )
53 } 43 }
54 44
55 async removeFollowing (follow: ActorFollow) { 45 async removeFollowing (follow: ActorFollow) {
diff --git a/client/src/app/+admin/follows/following-list/index.ts b/client/src/app/+admin/follows/following-list/index.ts
index a70d46a7e..88be0ed4c 100644
--- a/client/src/app/+admin/follows/following-list/index.ts
+++ b/client/src/app/+admin/follows/following-list/index.ts
@@ -1 +1,2 @@
1export * from './follow-modal.component'
1export * from './following-list.component' 2export * from './following-list.component'
diff --git a/client/src/app/+admin/follows/follows.routes.ts b/client/src/app/+admin/follows/follows.routes.ts
index cd70daf77..3843b42b5 100644
--- a/client/src/app/+admin/follows/follows.routes.ts
+++ b/client/src/app/+admin/follows/follows.routes.ts
@@ -25,7 +25,7 @@ export const FollowsRoutes: Routes = [
25 component: FollowingListComponent, 25 component: FollowingListComponent,
26 data: { 26 data: {
27 meta: { 27 meta: {
28 title: $localize`Following list` 28 title: $localize`Following`
29 } 29 }
30 } 30 }
31 }, 31 },
@@ -34,7 +34,7 @@ export const FollowsRoutes: Routes = [
34 component: FollowersListComponent, 34 component: FollowersListComponent,
35 data: { 35 data: {
36 meta: { 36 meta: {
37 title: $localize`Followers list` 37 title: $localize`Followers`
38 } 38 }
39 } 39 }
40 }, 40 },
diff --git a/client/src/app/+login/login.component.html b/client/src/app/+login/login.component.html
index 5f5b0f565..27793ff0c 100644
--- a/client/src/app/+login/login.component.html
+++ b/client/src/app/+login/login.component.html
@@ -28,6 +28,10 @@
28 <div *ngIf="formErrors.username" class="form-error"> 28 <div *ngIf="formErrors.username" class="form-error">
29 {{ formErrors.username }} 29 {{ formErrors.username }}
30 </div> 30 </div>
31
32 <div *ngIf="hasUsernameUppercase()" i18n class="form-warning">
33 ⚠️ Most email addresses do not include capital letters.
34 </div>
31 </div> 35 </div>
32 36
33 <div class="form-group"> 37 <div class="form-group">
diff --git a/client/src/app/+login/login.component.ts b/client/src/app/+login/login.component.ts
index d8ad49081..9731383af 100644
--- a/client/src/app/+login/login.component.ts
+++ b/client/src/app/+login/login.component.ts
@@ -141,6 +141,10 @@ The link will expire within 1 hour.`
141 this.accordion = instanceAboutAccordion.accordion 141 this.accordion = instanceAboutAccordion.accordion
142 } 142 }
143 143
144 hasUsernameUppercase () {
145 return this.form.value['username'].match(/[A-Z]/)
146 }
147
144 private loadExternalAuthToken (username: string, token: string) { 148 private loadExternalAuthToken (username: string, token: string) {
145 this.isAuthenticatedWithExternalAuth = true 149 this.isAuthenticatedWithExternalAuth = true
146 150
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
index b3265210f..433475f66 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
@@ -1,3 +1,5 @@
1import { of } from 'rxjs'
2import { switchMap } from 'rxjs/operators'
1import { Component, OnInit } from '@angular/core' 3import { Component, OnInit } from '@angular/core'
2import { Router } from '@angular/router' 4import { Router } from '@angular/router'
3import { AuthService, Notifier } from '@app/core' 5import { AuthService, Notifier } from '@app/core'
@@ -9,11 +11,8 @@ import {
9} from '@app/shared/form-validators/video-channel-validators' 11} from '@app/shared/form-validators/video-channel-validators'
10import { FormValidatorService } from '@app/shared/shared-forms' 12import { FormValidatorService } from '@app/shared/shared-forms'
11import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' 13import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
12import { VideoChannelCreate } from '@shared/models' 14import { HttpStatusCode, VideoChannelCreate } from '@shared/models'
13import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
14import { MyVideoChannelEdit } from './my-video-channel-edit' 15import { MyVideoChannelEdit } from './my-video-channel-edit'
15import { switchMap } from 'rxjs/operators'
16import { of } from 'rxjs'
17 16
18@Component({ 17@Component({
19 templateUrl: './my-video-channel-edit.component.html', 18 templateUrl: './my-video-channel-edit.component.html',
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
index 67b3ee496..b6a2f592d 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
@@ -45,9 +45,9 @@ export class MyVideoChannelsComponent {
45It will delete ${videoChannel.videosCount} videos uploaded in this channel, and you will not be able to create another 45It will delete ${videoChannel.videosCount} videos uploaded in this channel, and you will not be able to create another
46channel with the same name (${videoChannel.name})!`, 46channel with the same name (${videoChannel.name})!`,
47 47
48 $localize`Please type the display name of the video channel (${videoChannel.displayName}) to confirm`, 48 $localize`Please type the name of the video channel (${videoChannel.name}) to confirm`,
49 49
50 videoChannel.displayName, 50 videoChannel.name,
51 51
52 $localize`Delete` 52 $localize`Delete`
53 ) 53 )
diff --git a/client/src/app/+page-not-found/page-not-found.component.ts b/client/src/app/+page-not-found/page-not-found.component.ts
index 639e5db78..10645a634 100644
--- a/client/src/app/+page-not-found/page-not-found.component.ts
+++ b/client/src/app/+page-not-found/page-not-found.component.ts
@@ -1,7 +1,8 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { Title } from '@angular/platform-browser' 2import { Title } from '@angular/platform-browser'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '@shared/models'
5
5@Component({ 6@Component({
6 selector: 'my-page-not-found', 7 selector: 'my-page-not-found',
7 templateUrl: './page-not-found.component.html', 8 templateUrl: './page-not-found.component.html',
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts
index 3833d9c54..6479644f1 100644
--- a/client/src/app/+video-channels/video-channels.component.ts
+++ b/client/src/app/+video-channels/video-channels.component.ts
@@ -7,7 +7,7 @@ import { AuthService, MarkdownService, Notifier, RestExtractor, ScreenService }
7import { ListOverflowItem, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' 7import { ListOverflowItem, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
8import { SupportModalComponent } from '@app/shared/shared-support-modal' 8import { SupportModalComponent } from '@app/shared/shared-support-modal'
9import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 9import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
10import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 10import { HttpStatusCode } from '@shared/models'
11 11
12@Component({ 12@Component({
13 templateUrl: './video-channels.component.html', 13 templateUrl: './video-channels.component.html',
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
index 627de33c0..e9420fe62 100644
--- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
@@ -7,8 +7,7 @@ import { genericUploadErrorHandler, scrollToTop } from '@app/helpers'
7import { FormValidatorService } from '@app/shared/shared-forms' 7import { FormValidatorService } from '@app/shared/shared-forms'
8import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' 8import { BytesPipe, Video, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
9import { LoadingBarService } from '@ngx-loading-bar/core' 9import { LoadingBarService } from '@ngx-loading-bar/core'
10import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 10import { HttpStatusCode, VideoPrivacy } from '@shared/models'
11import { VideoPrivacy } from '@shared/models'
12import { UploaderXFormData } from './uploaderx-form-data' 11import { UploaderXFormData } from './uploaderx-form-data'
13import { VideoSend } from './video-send' 12import { VideoSend } from './video-send'
14 13
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts
index d078844c3..7460ae3fc 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -21,8 +21,15 @@ import { isXPercentInViewport, scrollToTop } from '@app/helpers'
21import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' 21import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
22import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 22import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
23import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 23import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
24import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 24import {
25import { HTMLServerConfig, PeerTubeProblemDocument, ServerErrorCode, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' 25 HTMLServerConfig,
26 HttpStatusCode,
27 PeerTubeProblemDocument,
28 ServerErrorCode,
29 VideoCaption,
30 VideoPrivacy,
31 VideoState
32} from '@shared/models'
26import { cleanupVideoWatch, getStoredTheater, getStoredVideoWatchHistory } from '../../../assets/player/peertube-player-local-storage' 33import { cleanupVideoWatch, getStoredTheater, getStoredVideoWatchHistory } from '../../../assets/player/peertube-player-local-storage'
27import { 34import {
28 CustomizationOptions, 35 CustomizationOptions,
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index cdf13186b..60bd72c60 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -6,12 +6,11 @@ import { Injectable } from '@angular/core'
6import { Router } from '@angular/router' 6import { Router } from '@angular/router'
7import { Notifier } from '@app/core/notification/notifier.service' 7import { Notifier } from '@app/core/notification/notifier.service'
8import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index' 8import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
9import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' 9import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
10import { environment } from '../../../environments/environment' 10import { environment } from '../../../environments/environment'
11import { RestExtractor } from '../rest/rest-extractor.service' 11import { RestExtractor } from '../rest/rest-extractor.service'
12import { AuthStatus } from './auth-status.model' 12import { AuthStatus } from './auth-status.model'
13import { AuthUser } from './auth-user.model' 13import { AuthUser } from './auth-user.model'
14import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
15 14
16interface UserLoginWithUsername extends UserLogin { 15interface UserLoginWithUsername extends UserLogin {
17 access_token: string 16 access_token: string
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts
index 08ab49512..2a926e68f 100644
--- a/client/src/app/core/rest/rest-extractor.service.ts
+++ b/client/src/app/core/rest/rest-extractor.service.ts
@@ -2,8 +2,7 @@ import { throwError as observableThrowError } from 'rxjs'
2import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
3import { Router } from '@angular/router' 3import { Router } from '@angular/router'
4import { dateToHuman } from '@app/helpers' 4import { dateToHuman } from '@app/helpers'
5import { ResultList } from '@shared/models' 5import { HttpStatusCode, ResultList } from '@shared/models'
6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
7 6
8@Injectable() 7@Injectable()
9export class RestExtractor { 8export class RestExtractor {
diff --git a/client/src/app/helpers/utils.ts b/client/src/app/helpers/utils.ts
index 94f6def26..edcaf50e0 100644
--- a/client/src/app/helpers/utils.ts
+++ b/client/src/app/helpers/utils.ts
@@ -3,7 +3,7 @@ import { SelectChannelItem } from 'src/types/select-options-item.model'
3import { DatePipe } from '@angular/common' 3import { DatePipe } from '@angular/common'
4import { HttpErrorResponse } from '@angular/common/http' 4import { HttpErrorResponse } from '@angular/common/http'
5import { Notifier } from '@app/core' 5import { Notifier } from '@app/core'
6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '@shared/models'
7import { environment } from '../../environments/environment' 7import { environment } from '../../environments/environment'
8import { AuthService } from '../core/auth' 8import { AuthService } from '../core/auth'
9 9
diff --git a/client/src/app/shared/form-validators/batch-domains-validators.ts b/client/src/app/shared/form-validators/batch-domains-validators.ts
deleted file mode 100644
index 423d1337f..000000000
--- a/client/src/app/shared/form-validators/batch-domains-validators.ts
+++ /dev/null
@@ -1,60 +0,0 @@
1import { AbstractControl, FormControl, ValidatorFn, Validators } from '@angular/forms'
2import { BuildFormValidator } from './form-validator.model'
3import { validateHost } from './host'
4
5export function getNotEmptyHosts (hosts: string) {
6 return hosts
7 .split('\n')
8 .filter((host: string) => host && host.length !== 0) // Eject empty hosts
9}
10
11const validDomains: ValidatorFn = (control: FormControl) => {
12 if (!control.value) return null
13
14 const newHostsErrors = []
15 const hosts = getNotEmptyHosts(control.value)
16
17 for (const host of hosts) {
18 if (validateHost(host) === false) {
19 newHostsErrors.push($localize`${host} is not valid`)
20 }
21 }
22
23 /* Is not valid. */
24 if (newHostsErrors.length !== 0) {
25 return {
26 'validDomains': {
27 reason: 'invalid',
28 value: newHostsErrors.join('. ') + '.'
29 }
30 }
31 }
32
33 /* Is valid. */
34 return null
35}
36
37const isHostsUnique: ValidatorFn = (control: AbstractControl) => {
38 if (!control.value) return null
39
40 const hosts = getNotEmptyHosts(control.value)
41
42 if (hosts.every((host: string) => hosts.indexOf(host) === hosts.lastIndexOf(host))) {
43 return null
44 } else {
45 return {
46 'uniqueDomains': {
47 reason: 'invalid'
48 }
49 }
50 }
51}
52
53export const DOMAINS_VALIDATOR: BuildFormValidator = {
54 VALIDATORS: [Validators.required, validDomains, isHostsUnique],
55 MESSAGES: {
56 'required': $localize`Domain is required.`,
57 'validDomains': $localize`Domains entered are invalid.`,
58 'uniqueDomains': $localize`Domains entered contain duplicates.`
59 }
60}
diff --git a/client/src/app/shared/form-validators/host-validators.ts b/client/src/app/shared/form-validators/host-validators.ts
new file mode 100644
index 000000000..d750113ef
--- /dev/null
+++ b/client/src/app/shared/form-validators/host-validators.ts
@@ -0,0 +1,105 @@
1import { AbstractControl, ValidatorFn, Validators } from '@angular/forms'
2import { BuildFormValidator } from './form-validator.model'
3
4function validateHost (value: string) {
5 // Thanks to http://stackoverflow.com/a/106223
6 const HOST_REGEXP = new RegExp(
7 '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'
8 )
9
10 return HOST_REGEXP.test(value)
11}
12
13function validateHandle (value: string) {
14 if (!value) return false
15
16 return value.includes('@')
17}
18
19const validHosts: ValidatorFn = (control: AbstractControl) => {
20 if (!control.value) return null
21
22 const errors = []
23 const hosts = splitAndGetNotEmpty(control.value)
24
25 for (const host of hosts) {
26 if (validateHost(host) === false) {
27 errors.push($localize`${host} is not valid`)
28 }
29 }
30
31 // valid
32 if (errors.length === 0) return null
33
34 return {
35 'validHosts': {
36 reason: 'invalid',
37 value: errors.join('. ') + '.'
38 }
39 }
40}
41
42const validHostsOrHandles: ValidatorFn = (control: AbstractControl) => {
43 if (!control.value) return null
44
45 const errors = []
46 const lines = splitAndGetNotEmpty(control.value)
47
48 for (const line of lines) {
49 if (validateHost(line) === false && validateHandle(line) === false) {
50 errors.push($localize`${line} is not valid`)
51 }
52 }
53
54 // valid
55 if (errors.length === 0) return null
56
57 return {
58 'validHostsOrHandles': {
59 reason: 'invalid',
60 value: errors.join('. ') + '.'
61 }
62 }
63}
64
65// ---------------------------------------------------------------------------
66
67export function splitAndGetNotEmpty (value: string) {
68 return value
69 .split('\n')
70 .filter(line => line && line.length !== 0) // Eject empty hosts
71}
72
73export const unique: ValidatorFn = (control: AbstractControl) => {
74 if (!control.value) return null
75
76 const hosts = splitAndGetNotEmpty(control.value)
77
78 if (hosts.every((host: string) => hosts.indexOf(host) === hosts.lastIndexOf(host))) {
79 return null
80 }
81
82 return {
83 'unique': {
84 reason: 'invalid'
85 }
86 }
87}
88
89export const UNIQUE_HOSTS_VALIDATOR: BuildFormValidator = {
90 VALIDATORS: [ Validators.required, validHosts, unique ],
91 MESSAGES: {
92 'required': $localize`Domain is required.`,
93 'validHosts': $localize`Hosts entered are invalid.`,
94 'unique': $localize`Hosts entered contain duplicates.`
95 }
96}
97
98export const UNIQUE_HOSTS_OR_HANDLE_VALIDATOR: BuildFormValidator = {
99 VALIDATORS: [ Validators.required, validHostsOrHandles, unique ],
100 MESSAGES: {
101 'required': $localize`Domain is required.`,
102 'validHostsOrHandles': $localize`Hosts or handles are invalid.`,
103 'unique': $localize`Hosts or handles contain duplicates.`
104 }
105}
diff --git a/client/src/app/shared/form-validators/host.ts b/client/src/app/shared/form-validators/host.ts
deleted file mode 100644
index c18a35f9b..000000000
--- a/client/src/app/shared/form-validators/host.ts
+++ /dev/null
@@ -1,8 +0,0 @@
1export function validateHost (value: string) {
2 // Thanks to http://stackoverflow.com/a/106223
3 const HOST_REGEXP = new RegExp(
4 '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'
5 )
6
7 return HOST_REGEXP.test(value)
8}
diff --git a/client/src/app/shared/form-validators/index.ts b/client/src/app/shared/form-validators/index.ts
index f621f03a4..0b605719c 100644
--- a/client/src/app/shared/form-validators/index.ts
+++ b/client/src/app/shared/form-validators/index.ts
@@ -1,7 +1,6 @@
1export * from './form-validator.model' 1export * from './form-validator.model'
2export * from './host'
3 2
4// Don't re export const variables because webpack 4 cannot do tree shaking with them 3// Don't re export const variables because webpack cannot do tree shaking with them
5// export * from './abuse-validators' 4// export * from './abuse-validators'
6// export * from './batch-domains-validators' 5// export * from './batch-domains-validators'
7// export * from './custom-config-validators' 6// export * from './custom-config-validators'
diff --git a/client/src/app/shared/shared-instance/instance-follow.service.ts b/client/src/app/shared/shared-instance/instance-follow.service.ts
index e52660140..af44020cf 100644
--- a/client/src/app/shared/shared-instance/instance-follow.service.ts
+++ b/client/src/app/shared/shared-instance/instance-follow.service.ts
@@ -4,7 +4,7 @@ import { catchError, map } from 'rxjs/operators'
4import { HttpClient, HttpParams } from '@angular/common/http' 4import { HttpClient, HttpParams } from '@angular/common/http'
5import { Injectable } from '@angular/core' 5import { Injectable } from '@angular/core'
6import { RestExtractor, RestPagination, RestService } from '@app/core' 6import { RestExtractor, RestPagination, RestService } from '@app/core'
7import { ActivityPubActorType, ActorFollow, FollowState, ResultList } from '@shared/models' 7import { ActivityPubActorType, ActorFollow, FollowState, ResultList, ServerFollowCreate } from '@shared/models'
8import { environment } from '../../../environments/environment' 8import { environment } from '../../../environments/environment'
9 9
10@Injectable() 10@Injectable()
@@ -64,9 +64,10 @@ export class InstanceFollowService {
64 ) 64 )
65 } 65 }
66 66
67 follow (notEmptyHosts: string[]) { 67 follow (hostsOrHandles: string[]) {
68 const body = { 68 const body: ServerFollowCreate = {
69 hosts: notEmptyHosts 69 handles: hostsOrHandles.filter(v => v.includes('@')),
70 hosts: hostsOrHandles.filter(v => !v.includes('@'))
70 } 71 }
71 72
72 return this.authHttp.post(InstanceFollowService.BASE_APPLICATION_URL + '/following', body) 73 return this.authHttp.post(InstanceFollowService.BASE_APPLICATION_URL + '/following', body)
@@ -77,7 +78,9 @@ export class InstanceFollowService {
77 } 78 }
78 79
79 unfollow (follow: ActorFollow) { 80 unfollow (follow: ActorFollow) {
80 return this.authHttp.delete(InstanceFollowService.BASE_APPLICATION_URL + '/following/' + follow.following.host) 81 const handle = follow.following.name + '@' + follow.following.host
82
83 return this.authHttp.delete(InstanceFollowService.BASE_APPLICATION_URL + '/following/' + handle)
81 .pipe( 84 .pipe(
82 map(this.restExtractor.extractDataBool), 85 map(this.restExtractor.extractDataBool),
83 catchError(res => this.restExtractor.handleError(res)) 86 catchError(res => this.restExtractor.handleError(res))
diff --git a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts
index 5bcad36d0..a75c8a25c 100644
--- a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts
+++ b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts
@@ -1,11 +1,11 @@
1import { Observable, of, throwError as observableThrowError } from 'rxjs' 1import { Observable, of, throwError as observableThrowError } from 'rxjs'
2import { catchError, switchMap } from 'rxjs/operators' 2import { catchError, switchMap } from 'rxjs/operators'
3import { HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse } from '@angular/common/http' 3import { HTTP_INTERCEPTORS, HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'
4import { Injectable, Injector } from '@angular/core' 4import { Injectable, Injector } from '@angular/core'
5import { AuthService } from '@app/core/auth/auth.service'
6import { Router } from '@angular/router' 5import { Router } from '@angular/router'
7import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 6import { AuthService } from '@app/core/auth/auth.service'
8import { OAuth2ErrorCode, PeerTubeProblemDocument, ServerErrorCode } from '@shared/models/server' 7import { HttpStatusCode } from '@shared/models'
8import { OAuth2ErrorCode, PeerTubeProblemDocument } from '@shared/models/server'
9 9
10@Injectable() 10@Injectable()
11export class AuthInterceptor implements HttpInterceptor { 11export class AuthInterceptor implements HttpInterceptor {
diff --git a/client/src/app/shared/shared-moderation/batch-domains-modal.component.html b/client/src/app/shared/shared-moderation/batch-domains-modal.component.html
index 6a3c65721..8306a96bc 100644
--- a/client/src/app/shared/shared-moderation/batch-domains-modal.component.html
+++ b/client/src/app/shared/shared-moderation/batch-domains-modal.component.html
@@ -1,6 +1,6 @@
1<ng-template #modal> 1<ng-template #modal>
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">{{ action }}</h4> 3 <h4 class="modal-title">{{ action }}</h4>
4 4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon> 5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
6 </div> 6 </div>
@@ -11,15 +11,15 @@
11 <label i18n for="hosts">1 host (without "http://") per line</label> 11 <label i18n for="hosts">1 host (without "http://") per line</label>
12 12
13 <textarea 13 <textarea
14 [placeholder]="placeholder" formControlName="domains" type="text" id="hosts" name="hosts" 14 [placeholder]="placeholder" formControlName="hosts" type="text" id="hosts" name="hosts"
15 class="form-control" [ngClass]="{ 'input-error': formErrors['domains'] }" ngbAutofocus 15 class="form-control" [ngClass]="{ 'input-error': formErrors['hosts'] }" ngbAutofocus
16 ></textarea> 16 ></textarea>
17 17
18 <div *ngIf="formErrors.domains" class="form-error"> 18 <div *ngIf="formErrors.hosts" class="form-error">
19 {{ formErrors.domains }} 19 {{ formErrors.hosts }}
20 20
21 <div *ngIf="form.controls['domains'].errors.validDomains"> 21 <div *ngIf="form.controls['hosts'].errors.validHosts">
22 {{ form.controls['domains'].errors.validDomains.value }} 22 {{ form.controls['hosts'].errors.validHosts.value }}
23 </div> 23 </div>
24 </div> 24 </div>
25 </div> 25 </div>
diff --git a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts
index 6edbb6023..20be728f6 100644
--- a/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts
+++ b/client/src/app/shared/shared-moderation/batch-domains-modal.component.ts
@@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angu
2import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 2import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
3import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 3import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
4import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 4import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
5import { DOMAINS_VALIDATOR, getNotEmptyHosts } from '../form-validators/batch-domains-validators' 5import { splitAndGetNotEmpty, UNIQUE_HOSTS_VALIDATOR } from '../form-validators/host-validators'
6 6
7@Component({ 7@Component({
8 selector: 'my-batch-domains-modal', 8 selector: 'my-batch-domains-modal',
@@ -28,7 +28,7 @@ export class BatchDomainsModalComponent extends FormReactive implements OnInit {
28 if (!this.action) this.action = $localize`Process domains` 28 if (!this.action) this.action = $localize`Process domains`
29 29
30 this.buildForm({ 30 this.buildForm({
31 domains: DOMAINS_VALIDATOR 31 hosts: UNIQUE_HOSTS_VALIDATOR
32 }) 32 })
33 } 33 }
34 34
@@ -41,9 +41,7 @@ export class BatchDomainsModalComponent extends FormReactive implements OnInit {
41 } 41 }
42 42
43 submit () { 43 submit () {
44 this.domains.emit( 44 this.domains.emit(splitAndGetNotEmpty(this.form.controls['hosts'].value))
45 getNotEmptyHosts(this.form.controls['domains'].value)
46 )
47 this.form.reset() 45 this.form.reset()
48 this.hide() 46 this.hide()
49 } 47 }
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss
index 30d487b11..bd834db70 100644
--- a/client/src/sass/application.scss
+++ b/client/src/sass/application.scss
@@ -123,12 +123,16 @@ code {
123 vertical-align: middle; 123 vertical-align: middle;
124} 124}
125 125
126.form-error { 126.form-error,
127.form-warning {
127 display: block; 128 display: block;
128 color: $red;
129 margin-top: 5px; 129 margin-top: 5px;
130} 130}
131 131
132.form-error {
133 color: $red;
134}
135
132.input-error, 136.input-error,
133my-input-toggle-hidden ::ng-deep input { 137my-input-toggle-hidden ::ng-deep input {
134 border-color: $red !important; 138 border-color: $red !important;
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index e59d8b940..97437ce45 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -1,9 +1,9 @@
1import './embed.scss' 1import './embed.scss'
2import videojs from 'video.js' 2import videojs from 'video.js'
3import { peertubeTranslate } from '../../../../shared/core-utils/i18n' 3import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
4import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
5import { 4import {
6 HTMLServerConfig, 5 HTMLServerConfig,
6 HttpStatusCode,
7 OAuth2ErrorCode, 7 OAuth2ErrorCode,
8 ResultList, 8 ResultList,
9 UserRefreshToken, 9 UserRefreshToken,
diff --git a/scripts/benchmark.ts b/scripts/benchmark.ts
index 0cadb36d9..83b932909 100644
--- a/scripts/benchmark.ts
+++ b/scripts/benchmark.ts
@@ -1,22 +1,12 @@
1import { registerTSPaths } from '../server/helpers/register-ts-paths'
2registerTSPaths()
3
4import * as autocannon from 'autocannon' 1import * as autocannon from 'autocannon'
5import {
6 addVideoCommentReply,
7 addVideoCommentThread,
8 createVideoCaption,
9 flushAndRunServer,
10 getVideosList,
11 killallServers,
12 ServerInfo,
13 setAccessTokensToServers,
14 uploadVideo
15} from '@shared/extra-utils'
16import { Video, VideoPrivacy } from '@shared/models'
17import { writeJson } from 'fs-extra' 2import { writeJson } from 'fs-extra'
3import { createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
4import { Video, VideoPrivacy } from '@shared/models'
5import { registerTSPaths } from '../server/helpers/register-ts-paths'
6
7registerTSPaths()
18 8
19let server: ServerInfo 9let server: PeerTubeServer
20let video: Video 10let video: Video
21let threadId: number 11let threadId: number
22 12
@@ -25,7 +15,7 @@ const outfile = process.argv[2]
25run() 15run()
26 .catch(err => console.error(err)) 16 .catch(err => console.error(err))
27 .finally(() => { 17 .finally(() => {
28 if (server) killallServers([ server ]) 18 if (server) return killallServers([ server ])
29 }) 19 })
30 20
31function buildAuthorizationHeader () { 21function buildAuthorizationHeader () {
@@ -198,7 +188,7 @@ function runBenchmark (options: {
198} 188}
199 189
200async function prepare () { 190async function prepare () {
201 server = await flushAndRunServer(1, { 191 server = await createSingleServer(1, {
202 rates_limit: { 192 rates_limit: {
203 api: { 193 api: {
204 max: 5_000_000 194 max: 5_000_000
@@ -207,7 +197,7 @@ async function prepare () {
207 }) 197 })
208 await setAccessTokensToServers([ server ]) 198 await setAccessTokensToServers([ server ])
209 199
210 const videoAttributes = { 200 const attributes = {
211 name: 'my super video', 201 name: 'my super video',
212 category: 2, 202 category: 2,
213 nsfw: true, 203 nsfw: true,
@@ -220,33 +210,29 @@ async function prepare () {
220 } 210 }
221 211
222 for (let i = 0; i < 10; i++) { 212 for (let i = 0; i < 10; i++) {
223 Object.assign(videoAttributes, { name: 'my super video ' + i }) 213 await server.videos.upload({ attributes: { ...attributes, name: 'my super video ' + i } })
224 await uploadVideo(server.url, server.accessToken, videoAttributes)
225 } 214 }
226 215
227 const resVideos = await getVideosList(server.url) 216 const { data } = await server.videos.list()
228 video = resVideos.body.data.find(v => v.name === 'my super video 1') 217 video = data.find(v => v.name === 'my super video 1')
229 218
230 for (let i = 0; i < 10; i++) { 219 for (let i = 0; i < 10; i++) {
231 const text = 'my super first comment' 220 const text = 'my super first comment'
232 const res = await addVideoCommentThread(server.url, server.accessToken, video.id, text) 221 const created = await server.comments.createThread({ videoId: video.id, text })
233 threadId = res.body.comment.id 222 threadId = created.id
234 223
235 const text1 = 'my super answer to thread 1' 224 const text1 = 'my super answer to thread 1'
236 const childCommentRes = await addVideoCommentReply(server.url, server.accessToken, video.id, threadId, text1) 225 const child = await server.comments.addReply({ videoId: video.id, toCommentId: threadId, text: text1 })
237 const childCommentId = childCommentRes.body.comment.id
238 226
239 const text2 = 'my super answer to answer of thread 1' 227 const text2 = 'my super answer to answer of thread 1'
240 await addVideoCommentReply(server.url, server.accessToken, video.id, childCommentId, text2) 228 await server.comments.addReply({ videoId: video.id, toCommentId: child.id, text: text2 })
241 229
242 const text3 = 'my second answer to thread 1' 230 const text3 = 'my second answer to thread 1'
243 await addVideoCommentReply(server.url, server.accessToken, video.id, threadId, text3) 231 await server.comments.addReply({ videoId: video.id, toCommentId: threadId, text: text3 })
244 } 232 }
245 233
246 for (const caption of [ 'ar', 'fr', 'en', 'zh' ]) { 234 for (const caption of [ 'ar', 'fr', 'en', 'zh' ]) {
247 await createVideoCaption({ 235 await server.captions.add({
248 url: server.url,
249 accessToken: server.accessToken,
250 language: caption, 236 language: caption,
251 videoId: video.id, 237 videoId: video.id,
252 fixture: 'subtitle-good2.vtt' 238 fixture: 'subtitle-good2.vtt'
diff --git a/scripts/generate-code-contributors.ts b/scripts/generate-code-contributors.ts
index db5af3f91..935ed3c5a 100755
--- a/scripts/generate-code-contributors.ts
+++ b/scripts/generate-code-contributors.ts
@@ -1,7 +1,7 @@
1import { registerTSPaths } from '../server/helpers/register-ts-paths' 1import { registerTSPaths } from '../server/helpers/register-ts-paths'
2registerTSPaths() 2registerTSPaths()
3 3
4import { execCLI } from '@shared/extra-utils' 4import { CLICommand } from '@shared/extra-utils'
5 5
6run() 6run()
7 .then(() => process.exit(0)) 7 .then(() => process.exit(0))
@@ -59,7 +59,7 @@ async function run () {
59} 59}
60 60
61async function getGitContributors () { 61async function getGitContributors () {
62 const output = await execCLI(`git --no-pager shortlog -sn < /dev/tty | sed 's/^\\s\\+[0-9]\\+\\s\\+//g'`) 62 const output = await CLICommand.exec(`git --no-pager shortlog -sn < /dev/tty | sed 's/^\\s\\+[0-9]\\+\\s\\+//g'`)
63 63
64 return output.split('\n') 64 return output.split('\n')
65 .filter(l => !!l) 65 .filter(l => !!l)
diff --git a/server.ts b/server.ts
index e46300dce..582321a5b 100644
--- a/server.ts
+++ b/server.ts
@@ -125,7 +125,7 @@ import { PeerTubeVersionCheckScheduler } from './server/lib/schedulers/peertube-
125import { Hooks } from './server/lib/plugins/hooks' 125import { Hooks } from './server/lib/plugins/hooks'
126import { PluginManager } from './server/lib/plugins/plugin-manager' 126import { PluginManager } from './server/lib/plugins/plugin-manager'
127import { LiveManager } from './server/lib/live' 127import { LiveManager } from './server/lib/live'
128import { HttpStatusCode } from './shared/core-utils/miscs/http-error-codes' 128import { HttpStatusCode } from './shared/models/http/http-error-codes'
129import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' 129import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
130import { ServerConfigManager } from '@server/lib/server-config-manager' 130import { ServerConfigManager } from '@server/lib/server-config-manager'
131 131
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/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..aaa341d54 100644
--- a/server/controllers/api/overviews.ts
+++ b/server/controllers/api/overviews.ts
@@ -1,12 +1,12 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as memoizee from 'memoizee'
3import { logger } from '@server/helpers/logger'
4import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews'
2import { buildNSFWFilter } from '../../helpers/express-utils' 5import { buildNSFWFilter } from '../../helpers/express-utils'
3import { VideoModel } from '../../models/video/video' 6import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants'
4import { asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares' 7import { asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares'
5import { TagModel } from '../../models/video/tag' 8import { TagModel } from '../../models/video/tag'
6import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews' 9import { VideoModel } from '../../models/video/video'
7import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants'
8import * as memoizee from 'memoizee'
9import { logger } from '@server/helpers/logger'
10 10
11const overviewsRouter = express.Router() 11const overviewsRouter = express.Router()
12 12
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..c8f0a0a0b 100644
--- a/server/controllers/api/search/search-video-channels.ts
+++ b/server/controllers/api/search/search-video-channels.ts
@@ -6,8 +6,7 @@ import { WEBSERVER } from '@server/initializers/constants'
6import { Hooks } from '@server/lib/plugins/hooks' 6import { Hooks } from '@server/lib/plugins/hooks'
7import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search' 7import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
8import { getServerActor } from '@server/models/application/application' 8import { getServerActor } from '@server/models/application/application'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 9import { HttpStatusCode, ResultList, VideoChannel } from '@shared/models'
10import { ResultList, VideoChannel } from '@shared/models'
11import { VideoChannelsSearchQuery } from '../../../../shared/models/search' 10import { VideoChannelsSearchQuery } from '../../../../shared/models/search'
12import { isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils' 11import { isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils'
13import { logger } from '../../../helpers/logger' 12import { logger } from '../../../helpers/logger'
diff --git a/server/controllers/api/search/search-video-playlists.ts b/server/controllers/api/search/search-video-playlists.ts
index b231ff1e2..f55b1fba3 100644
--- a/server/controllers/api/search/search-video-playlists.ts
+++ b/server/controllers/api/search/search-video-playlists.ts
@@ -5,14 +5,14 @@ import { logger } from '@server/helpers/logger'
5import { doJSONRequest } from '@server/helpers/requests' 5import { doJSONRequest } from '@server/helpers/requests'
6import { getFormattedObjects } from '@server/helpers/utils' 6import { getFormattedObjects } from '@server/helpers/utils'
7import { CONFIG } from '@server/initializers/config' 7import { CONFIG } from '@server/initializers/config'
8import { WEBSERVER } from '@server/initializers/constants'
8import { getOrCreateAPVideoPlaylist } from '@server/lib/activitypub/playlists/get' 9import { getOrCreateAPVideoPlaylist } from '@server/lib/activitypub/playlists/get'
9import { Hooks } from '@server/lib/plugins/hooks' 10import { Hooks } from '@server/lib/plugins/hooks'
10import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search' 11import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
11import { getServerActor } from '@server/models/application/application' 12import { getServerActor } from '@server/models/application/application'
12import { VideoPlaylistModel } from '@server/models/video/video-playlist' 13import { VideoPlaylistModel } from '@server/models/video/video-playlist'
13import { MVideoPlaylistFullSummary } from '@server/types/models' 14import { MVideoPlaylistFullSummary } from '@server/types/models'
14import { HttpStatusCode } from '@shared/core-utils' 15import { HttpStatusCode, ResultList, VideoPlaylist, VideoPlaylistsSearchQuery } from '@shared/models'
15import { ResultList, VideoPlaylist, VideoPlaylistsSearchQuery } from '@shared/models'
16import { 16import {
17 asyncMiddleware, 17 asyncMiddleware,
18 openapiOperationDoc, 18 openapiOperationDoc,
@@ -23,7 +23,6 @@ import {
23 videoPlaylistsListSearchValidator, 23 videoPlaylistsListSearchValidator,
24 videoPlaylistsSearchSortValidator 24 videoPlaylistsSearchSortValidator
25} from '../../../middlewares' 25} from '../../../middlewares'
26import { WEBSERVER } from '@server/initializers/constants'
27 26
28const searchPlaylistsRouter = express.Router() 27const searchPlaylistsRouter = express.Router()
29 28
diff --git a/server/controllers/api/search/search-videos.ts b/server/controllers/api/search/search-videos.ts
index b626baa28..a4153f3f8 100644
--- a/server/controllers/api/search/search-videos.ts
+++ b/server/controllers/api/search/search-videos.ts
@@ -6,8 +6,7 @@ import { WEBSERVER } from '@server/initializers/constants'
6import { getOrCreateAPVideo } from '@server/lib/activitypub/videos' 6import { getOrCreateAPVideo } from '@server/lib/activitypub/videos'
7import { Hooks } from '@server/lib/plugins/hooks' 7import { Hooks } from '@server/lib/plugins/hooks'
8import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search' 8import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 9import { HttpStatusCode, ResultList, Video } from '@shared/models'
10import { ResultList, Video } from '@shared/models'
11import { VideosSearchQuery } from '../../../../shared/models/search' 10import { VideosSearchQuery } from '../../../../shared/models/search'
12import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils' 11import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils'
13import { logger } from '../../../helpers/logger' 12import { logger } from '../../../helpers/logger'
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..f78607d35 100644
--- a/server/controllers/api/server/logs.ts
+++ b/server/controllers/api/server/logs.ts
@@ -1,14 +1,14 @@
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 } from '@server/helpers/logger'
5import { mtimeSortFilesDesc } from '../../../../shared/core-utils/logs/logs'
9import { LogLevel } from '../../../../shared/models/server/log-level.type' 6import { LogLevel } from '../../../../shared/models/server/log-level.type'
7import { UserRight } from '../../../../shared/models/users'
10import { CONFIG } from '../../../initializers/config' 8import { CONFIG } from '../../../initializers/config'
11import { logger } from '@server/helpers/logger' 9import { AUDIT_LOG_FILENAME, LOG_FILENAME, MAX_LOGS_OUTPUT_CHARACTERS } from '../../../initializers/constants'
10import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares'
11import { getAuditLogsValidator, getLogsValidator } from '../../../middlewares/validators/logs'
12 12
13const logsRouter = express.Router() 13const logsRouter = express.Router()
14 14
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/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..84f519926 100644
--- a/server/controllers/api/users/my-subscriptions.ts
+++ b/server/controllers/api/users/my-subscriptions.ts
@@ -3,7 +3,7 @@ import * as express from 'express'
3import { sendUndoFollow } from '@server/lib/activitypub/send' 3import { sendUndoFollow } from '@server/lib/activitypub/send'
4import { VideoChannelModel } from '@server/models/video/video-channel' 4import { VideoChannelModel } from '@server/models/video/video-channel'
5import { VideosCommonQuery } from '@shared/models' 5import { VideosCommonQuery } from '@shared/models'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../../shared/models/http/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'
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..784f97b1e 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -3,7 +3,7 @@ import { Hooks } from '@server/lib/plugins/hooks'
3import { getServerActor } from '@server/models/application/application' 3import { getServerActor } from '@server/models/application/application'
4import { MChannelBannerAccountDefault } from '@server/types/models' 4import { MChannelBannerAccountDefault } from '@server/types/models'
5import { ActorImageType, VideoChannelCreate, VideoChannelUpdate, VideosCommonQuery } from '../../../shared' 5import { ActorImageType, VideoChannelCreate, VideoChannelUpdate, VideosCommonQuery } from '../../../shared'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
7import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' 7import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger'
8import { resetSequelizeInstance } from '../../helpers/database-utils' 8import { resetSequelizeInstance } from '../../helpers/database-utils'
9import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' 9import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
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..5a2ff81dc 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -6,7 +6,7 @@ import { openapiOperationDoc } from '@server/middlewares/doc'
6import { getServerActor } from '@server/models/application/application' 6import { getServerActor } from '@server/models/application/application'
7import { MVideoAccountLight } from '@server/types/models' 7import { MVideoAccountLight } from '@server/types/models'
8import { VideosCommonQuery } from '../../../../shared' 8import { VideosCommonQuery } from '../../../../shared'
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs' 9import { HttpStatusCode } from '../../../../shared/models'
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'
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..1603ef127 100644
--- a/server/controllers/api/videos/upload.ts
+++ b/server/controllers/api/videos/upload.ts
@@ -11,7 +11,7 @@ import { 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'
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/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/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..5900eaff3 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'
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/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-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..cbd7aa401 100644
--- a/server/helpers/database-utils.ts
+++ b/server/helpers/database-utils.ts
@@ -6,7 +6,7 @@ import { 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 => {
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/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/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/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/client-html.ts b/server/lib/client-html.ts
index 72194416d..c5d39445b 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'
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/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/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
index 0708ee8e8..e508b22a6 100644
--- a/server/middlewares/cache.ts
+++ b/server/middlewares/cache.ts
@@ -1,6 +1,6 @@
1import { Redis } from '../lib/redis'
2import * as apicache from 'apicache' 1import * as apicache from 'apicache'
3import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 2import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
3import { Redis } from '../lib/redis'
4 4
5// Ensure Redis is initialized 5// Ensure Redis is initialized
6Redis.Instance.init() 6Redis.Instance.init()
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/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/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..748b89f8f 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -3,7 +3,7 @@ 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' 9import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor'
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..ea10fe425 100644
--- a/server/middlewares/validators/videos/video-channels.ts
+++ b/server/middlewares/validators/videos/video-channels.ts
@@ -3,7 +3,7 @@ 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' 7import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
8import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' 8import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc'
9import { 9import {
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..374a59c50 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,
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/actor/actor-follow.ts b/server/models/actor/actor-follow.ts
index 3a09e51d6..83c00a22d 100644
--- a/server/models/actor/actor-follow.ts
+++ b/server/models/actor/actor-follow.ts
@@ -324,13 +324,13 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
324 324
325 const followWhere = state ? { state } : {} 325 const followWhere = state ? { state } : {}
326 const followingWhere: WhereOptions = {} 326 const followingWhere: WhereOptions = {}
327 const followingServerWhere: WhereOptions = {}
328 327
329 if (search) { 328 if (search) {
330 Object.assign(followingServerWhere, { 329 Object.assign(followWhere, {
331 host: { 330 [Op.or]: [
332 [Op.iLike]: '%' + search + '%' 331 searchAttribute(options.search, '$ActorFollowing.preferredUsername$'),
333 } 332 searchAttribute(options.search, '$ActorFollowing.Server.host$')
333 ]
334 }) 334 })
335 } 335 }
336 336
@@ -361,8 +361,7 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
361 include: [ 361 include: [
362 { 362 {
363 model: ServerModel, 363 model: ServerModel,
364 required: true, 364 required: true
365 where: followingServerWhere
366 } 365 }
367 ] 366 ]
368 } 367 }
@@ -391,13 +390,13 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
391 390
392 const followWhere = state ? { state } : {} 391 const followWhere = state ? { state } : {}
393 const followerWhere: WhereOptions = {} 392 const followerWhere: WhereOptions = {}
394 const followerServerWhere: WhereOptions = {}
395 393
396 if (search) { 394 if (search) {
397 Object.assign(followerServerWhere, { 395 Object.assign(followWhere, {
398 host: { 396 [Op.or]: [
399 [Op.iLike]: '%' + search + '%' 397 searchAttribute(search, '$ActorFollower.preferredUsername$'),
400 } 398 searchAttribute(search, '$ActorFollower.Server.host$')
399 ]
401 }) 400 })
402 } 401 }
403 402
@@ -420,8 +419,7 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
420 include: [ 419 include: [
421 { 420 {
422 model: ServerModel, 421 model: ServerModel,
423 required: true, 422 required: true
424 where: followerServerWhere
425 } 423 }
426 ] 424 ]
427 }, 425 },
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..054f71c8c 100644
--- a/server/models/video/sql/videos-id-list-query-builder.ts
+++ b/server/models/video/sql/videos-id-list-query-builder.ts
@@ -304,16 +304,16 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
304 private whereFollowerActorId (followerActorId: number, includeLocalVideos: boolean) { 304 private whereFollowerActorId (followerActorId: number, includeLocalVideos: boolean) {
305 let query = 305 let query =
306 '(' + 306 '(' +
307 ' EXISTS (' + 307 ' EXISTS (' + // Videos shared by actors we follow
308 ' SELECT 1 FROM "videoShare" ' + 308 ' SELECT 1 FROM "videoShare" ' +
309 ' INNER JOIN "actorFollow" "actorFollowShare" ON "actorFollowShare"."targetActorId" = "videoShare"."actorId" ' + 309 ' INNER JOIN "actorFollow" "actorFollowShare" ON "actorFollowShare"."targetActorId" = "videoShare"."actorId" ' +
310 ' AND "actorFollowShare"."actorId" = :followerActorId AND "actorFollowShare"."state" = \'accepted\' ' + 310 ' AND "actorFollowShare"."actorId" = :followerActorId AND "actorFollowShare"."state" = \'accepted\' ' +
311 ' WHERE "videoShare"."videoId" = "video"."id"' + 311 ' WHERE "videoShare"."videoId" = "video"."id"' +
312 ' )' + 312 ' )' +
313 ' OR' + 313 ' OR' +
314 ' EXISTS (' + 314 ' EXISTS (' + // Videos published by accounts we follow
315 ' SELECT 1 from "actorFollow" ' + 315 ' SELECT 1 from "actorFollow" ' +
316 ' WHERE "actorFollow"."targetActorId" = "videoChannel"."actorId" AND "actorFollow"."actorId" = :followerActorId ' + 316 ' WHERE "actorFollow"."targetActorId" = "account"."actorId" AND "actorFollow"."actorId" = :followerActorId ' +
317 ' AND "actorFollow"."state" = \'accepted\'' + 317 ' AND "actorFollow"."state" = \'accepted\'' +
318 ' )' 318 ' )'
319 319
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..72f2cbd8f 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.video = 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.video.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.video.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.video.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.video.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: { id: server.store.video.id }, 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.video.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.video.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.video.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.video.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..a3da54e1f 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
@@ -60,83 +58,83 @@ describe('Test videos API validator', function () {
60 }) 58 })
61 59
62 it('Should success with the correct parameters', async function () { 60 it('Should success 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 })
131 129
132 const customQuery2 = immutableAssign(query, { endDate: 'hello' }) 130 const customQuery2 = { ...query, endDate: 'hello' }
133 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 131 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
134 132
135 const customQuery3 = immutableAssign(query, { originallyPublishedStartDate: 'hello' }) 133 const customQuery3 = { ...query, originallyPublishedStartDate: 'hello' }
136 await makeGetRequest({ url: server.url, path, query: customQuery3, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 134 await makeGetRequest({ url: server.url, path, query: customQuery3, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
137 135
138 const customQuery4 = immutableAssign(query, { originallyPublishedEndDate: 'hello' }) 136 const customQuery4 = { ...query, originallyPublishedEndDate: 'hello' }
139 await makeGetRequest({ url: server.url, path, query: customQuery4, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 137 await makeGetRequest({ url: server.url, path, query: customQuery4, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
140 }) 138 })
141 }) 139 })
142 140
@@ -160,7 +158,7 @@ describe('Test videos API validator', function () {
160 }) 158 })
161 159
162 it('Should success with the correct parameters', async function () { 160 it('Should success with the correct parameters', async function () {
163 await makeGetRequest({ url: server.url, path, query, statusCodeExpected: HttpStatusCode.OK_200 }) 161 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 })
164 }) 162 })
165 }) 163 })
166 164
@@ -184,7 +182,7 @@ describe('Test videos API validator', function () {
184 }) 182 })
185 183
186 it('Should success with the correct parameters', async function () { 184 it('Should success with the correct parameters', async function () {
187 await makeGetRequest({ url: server.url, path, query, statusCodeExpected: HttpStatusCode.OK_200 }) 185 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 })
188 }) 186 })
189 }) 187 })
190 188
@@ -202,42 +200,42 @@ describe('Test videos API validator', function () {
202 200
203 for (const path of paths) { 201 for (const path of paths) {
204 { 202 {
205 const customQuery = immutableAssign(query, { searchTarget: 'hello' }) 203 const customQuery = { ...query, searchTarget: 'hello' }
206 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 204 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
207 } 205 }
208 206
209 { 207 {
210 const customQuery = immutableAssign(query, { searchTarget: undefined }) 208 const customQuery = { ...query, searchTarget: undefined }
211 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 209 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
212 } 210 }
213 211
214 { 212 {
215 const customQuery = immutableAssign(query, { searchTarget: 'local' }) 213 const customQuery = { ...query, searchTarget: 'local' }
216 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 214 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
217 } 215 }
218 216
219 { 217 {
220 const customQuery = immutableAssign(query, { searchTarget: 'search-index' }) 218 const customQuery = { ...query, searchTarget: 'search-index' }
221 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 219 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
222 } 220 }
223 221
224 await updateSearchIndex(server, true, true) 222 await updateSearchIndex(server, true, true)
225 223
226 { 224 {
227 const customQuery = immutableAssign(query, { searchTarget: 'local' }) 225 const customQuery = { ...query, searchTarget: 'local' }
228 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 226 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
229 } 227 }
230 228
231 { 229 {
232 const customQuery = immutableAssign(query, { searchTarget: 'search-index' }) 230 const customQuery = { ...query, searchTarget: 'search-index' }
233 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 231 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
234 } 232 }
235 233
236 await updateSearchIndex(server, true, false) 234 await updateSearchIndex(server, true, false)
237 235
238 { 236 {
239 const customQuery = immutableAssign(query, { searchTarget: 'local' }) 237 const customQuery = { ...query, searchTarget: 'local' }
240 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 238 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
241 } 239 }
242 240
243 await updateSearchIndex(server, false, false) 241 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..4c4a5cade 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.video = 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.video.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.video.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.video.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.video.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.video.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.video.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.video.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..d28c6a952 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.video = 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.video + '/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.video + '/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.video + '/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.video.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.video.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.video + '/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.video + '/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.video.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.video.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,53 @@ 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.video.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.video.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.video.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.video
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({ token: 'fake token', videoId: servers[0].store.video.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
239 }) 232 })
240 233
241 it('Should fail with a non admin user', async function () { 234 it('Should fail with a non admin user', async function () {
242 await removeVideoFromBlacklist(servers[0].url, userAccessToken2, servers[0].video.uuid, HttpStatusCode.FORBIDDEN_403) 235 await command.remove({ token: userAccessToken2, videoId: servers[0].store.video.uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
243 }) 236 })
244 237
245 it('Should fail with an incorrect id', async function () { 238 it('Should fail with an incorrect id', async function () {
246 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, 'hello', HttpStatusCode.BAD_REQUEST_400) 239 await command.remove({ videoId: 'hello', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
247 }) 240 })
248 241
249 it('Should fail with a not blacklisted video', async function () { 242 it('Should fail with a not blacklisted video', async function () {
250 // The video was not added to the blacklist so it should fail 243 // 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) 244 await command.remove({ videoId: notBlacklistedVideoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
252 }) 245 })
253 246
254 it('Should succeed with the correct params', async function () { 247 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) 248 await command.remove({ videoId: servers[0].store.video.uuid, expectedStatus: HttpStatusCode.NO_CONTENT_204 })
256 }) 249 })
257 }) 250 })
258 251
@@ -260,11 +253,11 @@ describe('Test video blacklist API validators', function () {
260 const basePath = '/api/v1/videos/blacklist/' 253 const basePath = '/api/v1/videos/blacklist/'
261 254
262 it('Should fail with a non authenticated user', async function () { 255 it('Should fail with a non authenticated user', async function () {
263 await getBlacklistedVideosList({ url: servers[0].url, token: 'fake token', specialStatus: HttpStatusCode.UNAUTHORIZED_401 }) 256 await servers[0].blacklist.list({ token: 'fake token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
264 }) 257 })
265 258
266 it('Should fail with a non admin user', async function () { 259 it('Should fail with a non admin user', async function () {
267 await getBlacklistedVideosList({ url: servers[0].url, token: userAccessToken2, specialStatus: HttpStatusCode.FORBIDDEN_403 }) 260 await servers[0].blacklist.list({ token: userAccessToken2, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
268 }) 261 })
269 262
270 it('Should fail with a bad start pagination', async function () { 263 it('Should fail with a bad start pagination', async function () {
@@ -280,16 +273,11 @@ describe('Test video blacklist API validators', function () {
280 }) 273 })
281 274
282 it('Should fail with an invalid type', async function () { 275 it('Should fail with an invalid type', async function () {
283 await getBlacklistedVideosList({ 276 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 }) 277 })
290 278
291 it('Should succeed with the correct parameters', async function () { 279 it('Should succeed with the correct parameters', async function () {
292 await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, type: VideoBlacklistType.MANUAL }) 280 await servers[0].blacklist.list({ type: VideoBlacklistType.MANUAL })
293 }) 281 })
294 }) 282 })
295 283
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..20346113d 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 checkLiveCleanup,
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,15 +32,13 @@ 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 }
@@ -57,14 +48,12 @@ describe('Test live constraints', function () {
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..bd15396ec 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,
10 checkLiveCleanup, 7 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
@@ -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,8 +174,8 @@ 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)
@@ -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,7 +196,7 @@ 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)
@@ -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,13 +248,13 @@ 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
@@ -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,8 +282,8 @@ 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)
@@ -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
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..4676a840a 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -4,73 +4,68 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path' 5import { 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,
11 buildServerDirectory,
12 checkLiveCleanup, 8 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
340 expect(res.body.total).to.equal(1)
341 expect(res.body.data).to.have.lengthOf(1)
342 326
343 const video: Video = res.body.data[0] 327 expect(total).to.equal(1)
328 expect(data).to.have.lengthOf(1)
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 378
393 expect(videos.find(v => v.uuid === liveVideoId)).to.exist 379 const video = await server.videos.get({ id: liveVideoId })
394
395 const resVideo = await getVideo(server.url, 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)
@@ -530,7 +521,7 @@ describe('Test live', function () {
530 } 521 }
531 522
532 const filename = `${video.uuid}-${resolution}-fragmented.mp4` 523 const filename = `${video.uuid}-${resolution}-fragmented.mp4`
533 const segmentPath = buildServerDirectory(servers[0], join('streaming-playlists', 'hls', video.uuid, filename)) 524 const segmentPath = servers[0].servers.buildDirectory(join('streaming-playlists', 'hls', video.uuid, filename))
534 525
535 const probe = await ffprobePromise(segmentPath) 526 const probe = await ffprobePromise(segmentPath)
536 const videoStream = await getVideoStreamFromFile(segmentPath, probe) 527 const videoStream = await getVideoStreamFromFile(segmentPath, probe)
@@ -557,13 +548,13 @@ describe('Test live', function () {
557 async function createLiveWrapper (saveReplay: boolean) { 548 async function createLiveWrapper (saveReplay: boolean) {
558 const liveAttributes = { 549 const liveAttributes = {
559 name: 'live video', 550 name: 'live video',
560 channelId: servers[0].videoChannel.id, 551 channelId: servers[0].store.channel.id,
561 privacy: VideoPrivacy.PUBLIC, 552 privacy: VideoPrivacy.PUBLIC,
562 saveReplay 553 saveReplay
563 } 554 }
564 555
565 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 556 const { uuid } = await commands[0].create({ fields: liveAttributes })
566 return res.body.video.uuid 557 return uuid
567 } 558 }
568 559
569 before(async function () { 560 before(async function () {
@@ -573,20 +564,20 @@ describe('Test live', function () {
573 liveVideoReplayId = await createLiveWrapper(true) 564 liveVideoReplayId = await createLiveWrapper(true)
574 565
575 await Promise.all([ 566 await Promise.all([
576 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId), 567 commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId }),
577 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoReplayId) 568 commands[0].sendRTMPStreamInVideo({ videoId: liveVideoReplayId })
578 ]) 569 ])
579 570
580 await Promise.all([ 571 await Promise.all([
581 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoId), 572 commands[0].waitUntilPublished({ videoId: liveVideoId }),
582 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId) 573 commands[0].waitUntilPublished({ videoId: liveVideoReplayId })
583 ]) 574 ])
584 575
585 await waitUntilLiveSegmentGeneration(servers[0], liveVideoId, 0, 2) 576 await commands[0].waitUntilSegmentGeneration({ videoUUID: liveVideoId, resolution: 0, segment: 2 })
586 await waitUntilLiveSegmentGeneration(servers[0], liveVideoReplayId, 0, 2) 577 await commands[0].waitUntilSegmentGeneration({ videoUUID: liveVideoReplayId, resolution: 0, segment: 2 })
587 578
588 await killallServers([ servers[0] ]) 579 await killallServers([ servers[0] ])
589 await reRunServer(servers[0]) 580 await servers[0].run()
590 581
591 await wait(5000) 582 await wait(5000)
592 }) 583 })
@@ -594,13 +585,13 @@ describe('Test live', function () {
594 it('Should cleanup lives', async function () { 585 it('Should cleanup lives', async function () {
595 this.timeout(60000) 586 this.timeout(60000)
596 587
597 await waitUntilLiveEnded(servers[0].url, servers[0].accessToken, liveVideoId) 588 await commands[0].waitUntilEnded({ videoId: liveVideoId })
598 }) 589 })
599 590
600 it('Should save a live replay', async function () { 591 it('Should save a live replay', async function () {
601 this.timeout(120000) 592 this.timeout(120000)
602 593
603 await waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId) 594 await commands[0].waitUntilPublished({ videoId: liveVideoReplayId })
604 }) 595 })
605 }) 596 })
606 597
diff --git a/server/tests/api/moderation/abuses.ts b/server/tests/api/moderation/abuses.ts
index fb765e7e3..8d6360eb3 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.video = data.find(video => video.name === 'my super name for server 1')
97 68 servers[1].store.video = 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.video.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.video.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.video.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.video.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.video.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.video.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.video.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.video.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.video.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.video = await servers[0].videos.quickUpload({ name: 'server 1' })
441 servers[1].video = await uploadVideoAndGetId({ server: servers[1], videoName: 'server 2' }) 406 servers[1].store.video = 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.video.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.video.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.video.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.video.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.video.id)
481 expect(abuse.comment.video.uuid).to.equal(servers[0].video.uuid) 446 expect(abuse.comment.video.uuid).to.equal(servers[0].store.video.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.video.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.video.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.video.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.video.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.video.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.video.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.video.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.video.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..b36ba11a9 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 () {
@@ -127,8 +118,8 @@ describe('Test admin notifications', function () {
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.PLUGIN, 'hello-world', 'presence')
@@ -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..cbb46e510 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,18 +45,17 @@ 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 } = 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, uuid, commentId, commentId, 'absence')
@@ -73,11 +64,10 @@ describe('Test comments notifications', function () {
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 } = 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, uuid, commentId, commentId, 'absence')
@@ -86,28 +76,26 @@ describe('Test comments notifications', function () {
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 } = 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, uuid, commentId, commentId, '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 } = 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, uuid, commentId, commentId, 'presence')
@@ -116,33 +104,29 @@ describe('Test comments notifications', function () {
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 } = 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
118 const commentId = data[0].id
132 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'presence') 119 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, '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 } = 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, uuid, commentId, threadId, 'presence')
@@ -151,24 +135,22 @@ describe('Test comments notifications', function () {
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 } = 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
@@ -179,10 +161,9 @@ describe('Test comments notifications', function () {
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
@@ -199,30 +180,19 @@ describe('Test comments notifications', function () {
199 server: servers[0], 180 server: servers[0],
200 emails, 181 emails,
201 socketNotifications: userNotifications, 182 socketNotifications: userNotifications,
202 token: userAccessToken 183 token: userToken
203 } 184 }
204 185
205 await updateMyUser({ 186 await servers[0].users.updateMe({ displayName: 'super root name' })
206 url: servers[0].url, 187 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 }) 188 })
217 189
218 it('Should not send a new mention comment notification if I mention the video owner', async function () { 190 it('Should not send a new mention comment notification if I mention the video owner', async function () {
219 this.timeout(10000) 191 this.timeout(10000)
220 192
221 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 193 const { uuid } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
222 const uuid = resVideo.body.video.uuid
223 194
224 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello') 195 const { id: commentId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
225 const commentId = resComment.body.comment.id
226 196
227 await waitJobs(servers) 197 await waitJobs(servers)
228 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence') 198 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence')
@@ -231,11 +201,9 @@ describe('Test comments notifications', function () {
231 it('Should not send a new mention comment notification if I mention myself', async function () { 201 it('Should not send a new mention comment notification if I mention myself', async function () {
232 this.timeout(10000) 202 this.timeout(10000)
233 203
234 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 204 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
235 const uuid = resVideo.body.video.uuid
236 205
237 const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, uuid, '@user_1 hello') 206 const { id: commentId } = await servers[0].comments.createThread({ token: userToken, videoId: uuid, text: '@user_1 hello' })
238 const commentId = resComment.body.comment.id
239 207
240 await waitJobs(servers) 208 await waitJobs(servers)
241 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence') 209 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence')
@@ -244,29 +212,25 @@ describe('Test comments notifications', function () {
244 it('Should not send a new mention notification if the account is muted', async function () { 212 it('Should not send a new mention notification if the account is muted', async function () {
245 this.timeout(10000) 213 this.timeout(10000)
246 214
247 await addAccountToAccountBlocklist(servers[0].url, userAccessToken, 'root') 215 await servers[0].blocklist.addToMyBlocklist({ token: userToken, account: 'root' })
248 216
249 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 217 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
250 const uuid = resVideo.body.video.uuid
251 218
252 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello') 219 const { id: commentId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
253 const commentId = resComment.body.comment.id
254 220
255 await waitJobs(servers) 221 await waitJobs(servers)
256 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence') 222 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence')
257 223
258 await removeAccountFromAccountBlocklist(servers[0].url, userAccessToken, 'root') 224 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken, account: 'root' })
259 }) 225 })
260 226
261 it('Should not send a new mention notification if the remote account mention a local account', async function () { 227 it('Should not send a new mention notification if the remote account mention a local account', async function () {
262 this.timeout(20000) 228 this.timeout(20000)
263 229
264 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 230 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
265 const uuid = resVideo.body.video.uuid
266 231
267 await waitJobs(servers) 232 await waitJobs(servers)
268 const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, '@user_1 hello') 233 const { id: threadId } = await servers[1].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
269 const threadId = resThread.body.comment.id
270 234
271 await waitJobs(servers) 235 await waitJobs(servers)
272 await checkCommentMention(baseParams, uuid, threadId, threadId, 'super root 2 name', 'absence') 236 await checkCommentMention(baseParams, uuid, threadId, threadId, 'super root 2 name', 'absence')
@@ -275,17 +239,14 @@ describe('Test comments notifications', function () {
275 it('Should send a new mention notification after local comments', async function () { 239 it('Should send a new mention notification after local comments', async function () {
276 this.timeout(10000) 240 this.timeout(10000)
277 241
278 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 242 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
279 const uuid = resVideo.body.video.uuid
280 243
281 const resThread = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello 1') 244 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hellotext: 1' })
282 const threadId = resThread.body.comment.id
283 245
284 await waitJobs(servers) 246 await waitJobs(servers)
285 await checkCommentMention(baseParams, uuid, threadId, threadId, 'super root name', 'presence') 247 await checkCommentMention(baseParams, uuid, threadId, threadId, 'super root name', 'presence')
286 248
287 const resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, uuid, threadId, 'hello 2 @user_1') 249 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 250
290 await waitJobs(servers) 251 await waitJobs(servers)
291 await checkCommentMention(baseParams, uuid, commentId, threadId, 'super root name', 'presence') 252 await checkCommentMention(baseParams, uuid, commentId, threadId, 'super root name', 'presence')
@@ -294,29 +255,27 @@ describe('Test comments notifications', function () {
294 it('Should send a new mention notification after remote comments', async function () { 255 it('Should send a new mention notification after remote comments', async function () {
295 this.timeout(20000) 256 this.timeout(20000)
296 257
297 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 258 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
298 const uuid = resVideo.body.video.uuid
299 259
300 await waitJobs(servers) 260 await waitJobs(servers)
301 261
302 const text1 = `hello @user_1@localhost:${servers[0].port} 1` 262 const text1 = `hello @user_1@localhost:${servers[0].port} 1`
303 const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, text1) 263 const { id: server2ThreadId } = await servers[1].comments.createThread({ videoId: uuid, text: text1 })
304 const server2ThreadId = resThread.body.comment.id
305 264
306 await waitJobs(servers) 265 await waitJobs(servers)
307 266
308 const resThread2 = await getVideoCommentThreads(servers[0].url, uuid, 0, 5) 267 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
309 expect(resThread2.body.data).to.have.lengthOf(1) 268 expect(data).to.have.lengthOf(1)
310 const server1ThreadId = resThread2.body.data[0].id 269
270 const server1ThreadId = data[0].id
311 await checkCommentMention(baseParams, uuid, server1ThreadId, server1ThreadId, 'super root 2 name', 'presence') 271 await checkCommentMention(baseParams, uuid, server1ThreadId, server1ThreadId, 'super root 2 name', 'presence')
312 272
313 const text2 = `@user_1@localhost:${servers[0].port} hello 2 @root@localhost:${servers[0].port}` 273 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) 274 await servers[1].comments.addReply({ videoId: uuid, toCommentId: server2ThreadId, text: text2 })
315 275
316 await waitJobs(servers) 276 await waitJobs(servers)
317 277
318 const resComments = await getVideoThreadComments(servers[0].url, uuid, server1ThreadId) 278 const tree = await servers[0].comments.getThread({ videoId: uuid, threadId: server1ThreadId })
319 const tree = resComments.body as VideoCommentThreadTree
320 279
321 expect(tree.children).to.have.lengthOf(1) 280 expect(tree.children).to.have.lengthOf(1)
322 const commentId = tree.children[0].comment.id 281 const commentId = tree.children[0].comment.id
@@ -327,13 +286,11 @@ describe('Test comments notifications', function () {
327 it('Should convert markdown in comment to html', async function () { 286 it('Should convert markdown in comment to html', async function () {
328 this.timeout(10000) 287 this.timeout(10000)
329 288
330 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 289 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
331 const uuid = resVideo.body.video.uuid
332 290
333 const resThread = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello 1') 291 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello 1' })
334 const threadId = resThread.body.comment.id
335 292
336 await addVideoCommentReply(servers[0].url, servers[0].accessToken, uuid, threadId, '@user_1 ' + commentText) 293 await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: '@user_1 ' + commentText })
337 294
338 await waitJobs(servers) 295 await waitJobs(servers)
339 296
diff --git a/server/tests/api/notifications/moderation-notifications.ts b/server/tests/api/notifications/moderation-notifications.ts
index 3425480ae..6f74709b3 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,10 +62,9 @@ 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, video.uuid, name, 'presence')
@@ -99,13 +74,12 @@ describe('Test moderation notifications', 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, video.uuid, name, 'presence')
@@ -115,14 +89,16 @@ describe('Test moderation notifications', 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, video.uuid, name, 'presence')
@@ -132,15 +108,19 @@ describe('Test moderation notifications', 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, video.uuid, name, 'presence')
@@ -150,10 +130,10 @@ describe('Test moderation notifications', 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, username, 'presence')
@@ -163,13 +143,13 @@ describe('Test moderation notifications', 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, username, 'presence')
@@ -189,17 +169,16 @@ 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, AbuseState.ACCEPTED, 'presence')
@@ -208,7 +187,7 @@ describe('Test moderation notifications', function () {
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, AbuseState.REJECTED, 'presence')
@@ -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,7 +233,7 @@ 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, 'user_1@example.com', 'presence')
@@ -265,7 +243,7 @@ describe('Test moderation notifications', 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 await checkNewAbuseMessage(baseParamsAdmin, abuseId, message, 'admin' + servers[0].internalServerNumber + '@example.com', 'absence')
@@ -275,7 +253,7 @@ describe('Test moderation notifications', function () {
275 this.timeout(10000) 253 this.timeout(10000)
276 254
277 const message = 'my super message to moderators' 255 const message = 'my super message to moderators'
278 await addAbuseMessage(servers[0].url, userAccessToken, abuseId2, message) 256 await servers[0].abuses.addMessage({ token: userAccessToken, abuseId: abuseId2, message })
279 await waitJobs(servers) 257 await waitJobs(servers)
280 258
281 await checkNewAbuseMessage(baseParamsAdmin, abuseId2, message, 'admin' + servers[0].internalServerNumber + '@example.com', 'presence') 259 await checkNewAbuseMessage(baseParamsAdmin, abuseId2, message, 'admin' + servers[0].internalServerNumber + '@example.com', 'presence')
@@ -285,7 +263,7 @@ describe('Test moderation notifications', function () {
285 this.timeout(10000) 263 this.timeout(10000)
286 264
287 const message = 'my super message that should not be sent to reporter' 265 const message = 'my super message that should not be sent to reporter'
288 await addAbuseMessage(servers[0].url, userAccessToken, abuseId2, message) 266 await servers[0].abuses.addMessage({ token: userAccessToken, abuseId: abuseId2, message })
289 await waitJobs(servers) 267 await waitJobs(servers)
290 268
291 await checkNewAbuseMessage(baseParamsUser, abuseId2, message, 'user_1@example.com', 'absence') 269 await checkNewAbuseMessage(baseParamsUser, abuseId2, message, 'user_1@example.com', 'absence')
@@ -308,10 +286,9 @@ describe('Test moderation notifications', function () {
308 this.timeout(10000) 286 this.timeout(10000)
309 287
310 const name = 'video for abuse ' + buildUUID() 288 const name = 'video for abuse ' + buildUUID()
311 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 289 const { uuid } = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
312 const uuid = resVideo.body.video.uuid
313 290
314 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) 291 await servers[0].blacklist.add({ videoId: uuid })
315 292
316 await waitJobs(servers) 293 await waitJobs(servers)
317 await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'blacklist') 294 await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'blacklist')
@@ -321,13 +298,12 @@ describe('Test moderation notifications', function () {
321 this.timeout(10000) 298 this.timeout(10000)
322 299
323 const name = 'video for abuse ' + buildUUID() 300 const name = 'video for abuse ' + buildUUID()
324 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 301 const { uuid } = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
325 const uuid = resVideo.body.video.uuid
326 302
327 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) 303 await servers[0].blacklist.add({ videoId: uuid })
328 304
329 await waitJobs(servers) 305 await waitJobs(servers)
330 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, uuid) 306 await servers[0].blacklist.remove({ videoId: uuid })
331 await waitJobs(servers) 307 await waitJobs(servers)
332 308
333 await wait(500) 309 await wait(500)
@@ -350,14 +326,14 @@ describe('Test moderation notifications', function () {
350 it('Should send a notification only to moderators when a user registers on the instance', async function () { 326 it('Should send a notification only to moderators when a user registers on the instance', async function () {
351 this.timeout(10000) 327 this.timeout(10000)
352 328
353 await registerUser(servers[0].url, 'user_45', 'password') 329 await servers[0].users.register({ username: 'user_45' })
354 330
355 await waitJobs(servers) 331 await waitJobs(servers)
356 332
357 await checkUserRegistered(baseParams, 'user_45', 'presence') 333 await checkUserRegistered(baseParams, 'user_45', 'presence')
358 334
359 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } 335 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } }
360 await checkUserRegistered(immutableAssign(baseParams, userOverride), 'user_45', 'absence') 336 await checkUserRegistered({ ...baseParams, ...userOverride }, 'user_45', 'absence')
361 }) 337 })
362 }) 338 })
363 339
@@ -392,20 +368,20 @@ describe('Test moderation notifications', function () {
392 it('Should send a notification only to admin when there is a new instance follower', async function () { 368 it('Should send a notification only to admin when there is a new instance follower', async function () {
393 this.timeout(20000) 369 this.timeout(20000)
394 370
395 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 371 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
396 372
397 await waitJobs(servers) 373 await waitJobs(servers)
398 374
399 await checkNewInstanceFollower(baseParams, 'localhost:' + servers[2].port, 'presence') 375 await checkNewInstanceFollower(baseParams, 'localhost:' + servers[2].port, 'presence')
400 376
401 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } 377 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } }
402 await checkNewInstanceFollower(immutableAssign(baseParams, userOverride), 'localhost:' + servers[2].port, 'absence') 378 await checkNewInstanceFollower({ ...baseParams, ...userOverride }, 'localhost:' + servers[2].port, 'absence')
403 }) 379 })
404 380
405 it('Should send a notification on auto follow back', async function () { 381 it('Should send a notification on auto follow back', async function () {
406 this.timeout(40000) 382 this.timeout(40000)
407 383
408 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 384 await servers[2].follows.unfollow({ target: servers[0] })
409 await waitJobs(servers) 385 await waitJobs(servers)
410 386
411 const config = { 387 const config = {
@@ -415,9 +391,9 @@ describe('Test moderation notifications', function () {
415 } 391 }
416 } 392 }
417 } 393 }
418 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 394 await servers[0].config.updateCustomSubConfig({ newConfig: config })
419 395
420 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 396 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
421 397
422 await waitJobs(servers) 398 await waitJobs(servers)
423 399
@@ -426,19 +402,19 @@ describe('Test moderation notifications', function () {
426 await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence') 402 await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence')
427 403
428 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } 404 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } }
429 await checkAutoInstanceFollowing(immutableAssign(baseParams, userOverride), followerHost, followingHost, 'absence') 405 await checkAutoInstanceFollowing({ ...baseParams, ...userOverride }, followerHost, followingHost, 'absence')
430 406
431 config.followings.instance.autoFollowBack.enabled = false 407 config.followings.instance.autoFollowBack.enabled = false
432 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 408 await servers[0].config.updateCustomSubConfig({ newConfig: config })
433 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 409 await servers[0].follows.unfollow({ target: servers[2] })
434 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 410 await servers[2].follows.unfollow({ target: servers[0] })
435 }) 411 })
436 412
437 it('Should send a notification on auto instances index follow', async function () { 413 it('Should send a notification on auto instances index follow', async function () {
438 this.timeout(30000) 414 this.timeout(30000)
439 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 415 await servers[0].follows.unfollow({ target: servers[1] })
440 416
441 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 417 await servers[0].config.updateCustomSubConfig({ newConfig: config })
442 418
443 await wait(5000) 419 await wait(5000)
444 await waitJobs(servers) 420 await waitJobs(servers)
@@ -448,8 +424,8 @@ describe('Test moderation notifications', function () {
448 await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence') 424 await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence')
449 425
450 config.followings.instance.autoFollowIndex.enabled = false 426 config.followings.instance.autoFollowIndex.enabled = false
451 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 427 await servers[0].config.updateCustomSubConfig({ newConfig: config })
452 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 428 await servers[0].follows.unfollow({ target: servers[1] })
453 }) 429 })
454 }) 430 })
455 431
@@ -484,9 +460,11 @@ describe('Test moderation notifications', function () {
484 token: userAccessToken 460 token: userAccessToken
485 } 461 }
486 462
487 const resCustomConfig = await getCustomConfig(servers[0].url, servers[0].accessToken) 463 currentCustomConfig = await servers[0].config.getCustomConfig()
488 currentCustomConfig = resCustomConfig.body 464
489 const autoBlacklistTestsCustomConfig = immutableAssign(currentCustomConfig, { 465 const autoBlacklistTestsCustomConfig = {
466 ...currentCustomConfig,
467
490 autoBlacklist: { 468 autoBlacklist: {
491 videos: { 469 videos: {
492 ofUsers: { 470 ofUsers: {
@@ -494,13 +472,14 @@ describe('Test moderation notifications', function () {
494 } 472 }
495 } 473 }
496 } 474 }
497 }) 475 }
476
498 // enable transcoding otherwise own publish notification after transcoding not expected 477 // enable transcoding otherwise own publish notification after transcoding not expected
499 autoBlacklistTestsCustomConfig.transcoding.enabled = true 478 autoBlacklistTestsCustomConfig.transcoding.enabled = true
500 await updateCustomConfig(servers[0].url, servers[0].accessToken, autoBlacklistTestsCustomConfig) 479 await servers[0].config.updateCustomConfig({ newCustomConfig: autoBlacklistTestsCustomConfig })
501 480
502 await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 481 await servers[0].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
503 await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) 482 await servers[1].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
504 483
505 }) 484 })
506 485
@@ -508,8 +487,8 @@ describe('Test moderation notifications', function () {
508 this.timeout(40000) 487 this.timeout(40000)
509 488
510 videoName = 'video with auto-blacklist ' + buildUUID() 489 videoName = 'video with auto-blacklist ' + buildUUID()
511 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName }) 490 const { uuid } = await servers[0].videos.upload({ token: userAccessToken, attributes: { name: videoName } })
512 videoUUID = resVideo.body.video.uuid 491 videoUUID = uuid
513 492
514 await waitJobs(servers) 493 await waitJobs(servers)
515 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, videoUUID, videoName, 'presence') 494 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, videoUUID, videoName, 'presence')
@@ -530,7 +509,7 @@ describe('Test moderation notifications', function () {
530 it('Should send video published and unblacklist after video unblacklisted', async function () { 509 it('Should send video published and unblacklist after video unblacklisted', async function () {
531 this.timeout(40000) 510 this.timeout(40000)
532 511
533 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoUUID) 512 await servers[0].blacklist.remove({ videoId: videoUUID })
534 513
535 await waitJobs(servers) 514 await waitJobs(servers)
536 515
@@ -555,19 +534,18 @@ describe('Test moderation notifications', function () {
555 534
556 const name = 'video with auto-blacklist and future schedule ' + buildUUID() 535 const name = 'video with auto-blacklist and future schedule ' + buildUUID()
557 536
558 const data = { 537 const attributes = {
559 name, 538 name,
560 privacy: VideoPrivacy.PRIVATE, 539 privacy: VideoPrivacy.PRIVATE,
561 scheduleUpdate: { 540 scheduleUpdate: {
562 updateAt: updateAt.toISOString(), 541 updateAt: updateAt.toISOString(),
563 privacy: VideoPrivacy.PUBLIC 542 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
564 } 543 }
565 } 544 }
566 545
567 const resVideo = await uploadVideo(servers[0].url, userAccessToken, data) 546 const { uuid } = await servers[0].videos.upload({ token: userAccessToken, attributes })
568 const uuid = resVideo.body.video.uuid
569 547
570 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, uuid) 548 await servers[0].blacklist.remove({ videoId: uuid })
571 549
572 await waitJobs(servers) 550 await waitJobs(servers)
573 await checkNewBlacklistOnMyVideo(userBaseParams, uuid, name, 'unblacklist') 551 await checkNewBlacklistOnMyVideo(userBaseParams, uuid, name, 'unblacklist')
@@ -588,17 +566,16 @@ describe('Test moderation notifications', function () {
588 566
589 const name = 'video with schedule done and still auto-blacklisted ' + buildUUID() 567 const name = 'video with schedule done and still auto-blacklisted ' + buildUUID()
590 568
591 const data = { 569 const attributes = {
592 name, 570 name,
593 privacy: VideoPrivacy.PRIVATE, 571 privacy: VideoPrivacy.PRIVATE,
594 scheduleUpdate: { 572 scheduleUpdate: {
595 updateAt: updateAt.toISOString(), 573 updateAt: updateAt.toISOString(),
596 privacy: VideoPrivacy.PUBLIC 574 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
597 } 575 }
598 } 576 }
599 577
600 const resVideo = await uploadVideo(servers[0].url, userAccessToken, data) 578 const { uuid } = await servers[0].videos.upload({ token: userAccessToken, attributes })
601 const uuid = resVideo.body.video.uuid
602 579
603 await wait(6000) 580 await wait(6000)
604 await checkVideoIsPublished(userBaseParams, name, uuid, 'absence') 581 await checkVideoIsPublished(userBaseParams, name, uuid, 'absence')
@@ -612,18 +589,17 @@ describe('Test moderation notifications', function () {
612 const name = 'video without auto-blacklist ' + buildUUID() 589 const name = 'video without auto-blacklist ' + buildUUID()
613 590
614 // admin with blacklist right will not be auto-blacklisted 591 // admin with blacklist right will not be auto-blacklisted
615 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name }) 592 const { uuid } = await servers[0].videos.upload({ attributes: { name } })
616 const uuid = resVideo.body.video.uuid
617 593
618 await waitJobs(servers) 594 await waitJobs(servers)
619 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, uuid, name, 'absence') 595 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, uuid, name, 'absence')
620 }) 596 })
621 597
622 after(async () => { 598 after(async () => {
623 await updateCustomConfig(servers[0].url, servers[0].accessToken, currentCustomConfig) 599 await servers[0].config.updateCustomConfig({ newCustomConfig: currentCustomConfig })
624 600
625 await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 601 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) 602 await servers[1].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
627 }) 603 })
628 }) 604 })
629 605
diff --git a/server/tests/api/notifications/notifications-api.ts b/server/tests/api/notifications/notifications-api.ts
index b81995449..fa4b53db6 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,97 +94,100 @@ 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, uuid } = 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 }, name, uuid, '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, uuid } = 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 }, name, uuid, '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 }, name, uuid, '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, uuid } = 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 }, name, uuid, '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 }, name, uuid, '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, uuid } = await server.videos.randomUpload()
195 191
196 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 192 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence')
197 }) 193 })
diff --git a/server/tests/api/notifications/user-notifications.ts b/server/tests/api/notifications/user-notifications.ts
index e981c1718..ca592d466 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,7 +71,7 @@ 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, uuid } = await uploadRandomVideoOnServers(servers, 1)
@@ -89,7 +81,7 @@ describe('Test user notifications', function () {
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, uuid } = await uploadRandomVideoOnServers(servers, 2)
@@ -106,7 +98,7 @@ 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, uuid } = await uploadRandomVideoOnServers(servers, 1, data)
@@ -125,7 +117,7 @@ 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, uuid } = await uploadRandomVideoOnServers(servers, 2, data)
@@ -144,7 +136,7 @@ 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, uuid } = await uploadRandomVideoOnServers(servers, 1, data)
@@ -161,7 +153,7 @@ describe('Test user notifications', function () {
161 153
162 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 154 await checkNewVideoFromSubscription(baseParams, name, uuid, '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, name, uuid, 'presence')
@@ -175,7 +167,7 @@ describe('Test user notifications', function () {
175 167
176 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 168 await checkNewVideoFromSubscription(baseParams, name, uuid, '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, name, uuid, 'presence')
@@ -187,7 +179,7 @@ describe('Test user notifications', function () {
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 } = 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, name, uuid, 'absence')
193 }) 185 })
@@ -198,7 +190,7 @@ describe('Test user notifications', function () {
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 } = 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, name, uuid, 'absence')
@@ -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, name, video.uuid, 'presence')
224 }) 215 })
225 }) 216 })
226 217
@@ -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 }
@@ -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, name, video.uuid, '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,7 +294,7 @@ 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, uuid } = await uploadRandomVideoOnServers(servers, 2, data)
@@ -322,7 +312,7 @@ 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, uuid } = await uploadRandomVideoOnServers(servers, 2, data)
@@ -353,13 +343,12 @@ 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 } = 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 await checkMyVideoImportIsFinished(baseParams, name, video.uuid, FIXTURE_URLS.badVideo, false, 'presence')
363 }) 352 })
364 353
365 it('Should send a notification when the video import succeeded', async function () { 354 it('Should send a notification when the video import succeeded', async function () {
@@ -371,13 +360,12 @@ describe('Test user notifications', function () {
371 name, 360 name,
372 channelId, 361 channelId,
373 privacy: VideoPrivacy.PRIVATE, 362 privacy: VideoPrivacy.PRIVATE,
374 targetUrl: getGoodVideoUrl() 363 targetUrl: FIXTURE_URLS.goodVideo
375 } 364 }
376 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 365 const { video } = await servers[0].imports.importVideo({ attributes })
377 const uuid = res.body.video.uuid
378 366
379 await waitJobs(servers) 367 await waitJobs(servers)
380 await checkMyVideoImportIsFinished(baseParams, name, uuid, getGoodVideoUrl(), true, 'presence') 368 await checkMyVideoImportIsFinished(baseParams, name, video.uuid, FIXTURE_URLS.goodVideo, true, 'presence')
381 }) 369 })
382 }) 370 })
383 371
@@ -394,47 +382,42 @@ describe('Test user notifications', function () {
394 token: userAccessToken 382 token: userAccessToken
395 } 383 }
396 384
397 await updateMyUser({ 385 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 386
403 await updateMyUser({ 387 await servers[0].users.updateMe({
404 url: servers[0].url, 388 token: userAccessToken,
405 accessToken: userAccessToken,
406 displayName: myUserName 389 displayName: myUserName
407 }) 390 })
408 391
409 await updateMyUser({ 392 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 393
415 await updateVideoChannel(servers[0].url, userAccessToken, 'user_1_channel', { displayName: myChannelName }) 394 await servers[0].channels.update({
395 token: userAccessToken,
396 channelName: 'user_1_channel',
397 attributes: { displayName: myChannelName }
398 })
416 }) 399 })
417 400
418 it('Should notify when a local channel is following one of our channel', async function () { 401 it('Should notify when a local channel is following one of our channel', async function () {
419 this.timeout(50000) 402 this.timeout(50000)
420 403
421 await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 404 await servers[0].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
422 await waitJobs(servers) 405 await waitJobs(servers)
423 406
424 await checkNewActorFollow(baseParams, 'channel', 'root', 'super root name', myChannelName, 'presence') 407 await checkNewActorFollow(baseParams, 'channel', 'root', 'super root name', myChannelName, 'presence')
425 408
426 await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 409 await servers[0].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
427 }) 410 })
428 411
429 it('Should notify when a remote channel is following one of our channel', async function () { 412 it('Should notify when a remote channel is following one of our channel', async function () {
430 this.timeout(50000) 413 this.timeout(50000)
431 414
432 await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) 415 await servers[1].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
433 await waitJobs(servers) 416 await waitJobs(servers)
434 417
435 await checkNewActorFollow(baseParams, 'channel', 'root', 'super root 2 name', myChannelName, 'presence') 418 await checkNewActorFollow(baseParams, 'channel', 'root', 'super root 2 name', myChannelName, 'presence')
436 419
437 await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) 420 await servers[1].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
438 }) 421 })
439 422
440 // PeerTube does not support accout -> account follows 423 // 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..a6559d304 100644
--- a/server/tests/api/redundancy/redundancy.ts
+++ b/server/tests/api/redundancy/redundancy.ts
@@ -5,51 +5,29 @@ import * 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 { 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 makeGetRequest,
21 removeVideo, 16 PeerTubeServer,
22 reRunServer,
23 root, 17 root,
24 ServerInfo,
25 setAccessTokensToServers, 18 setAccessTokensToServers,
26 unfollow,
27 updateVideo,
28 uploadVideo,
29 viewVideo,
30 wait, 19 wait,
31 waitUntilLog 20 waitJobs
32} from '../../../../shared/extra-utils' 21} from '@shared/extra-utils'
33import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 22import { HttpStatusCode, VideoPrivacy, VideoRedundancyStrategy, VideoRedundancyStrategyWithManual } from '@shared/models'
34import {
35 addVideoRedundancy,
36 listVideoRedundancies,
37 removeVideoRedundancy,
38 updateRedundancy
39} from '../../../../shared/extra-utils/server/redundancy'
40import { getStats } from '../../../../shared/extra-utils/server/stats'
41import { ActorFollow } from '../../../../shared/models/actors'
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 23
46const expect = chai.expect 24const expect = chai.expect
47 25
48let servers: ServerInfo[] = [] 26let servers: PeerTubeServer[] = []
49let video1Server2UUID: string 27let video1Server2UUID: string
50let video1Server2Id: number 28let video1Server2Id: number
51 29
52function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: number } }, baseWebseeds: string[], server: ServerInfo) { 30function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: number } }, baseWebseeds: string[], server: PeerTubeServer) {
53 const parsed = magnetUtil.decode(file.magnetUri) 31 const parsed = magnetUtil.decode(file.magnetUri)
54 32
55 for (const ws of baseWebseeds) { 33 for (const ws of baseWebseeds) {
@@ -60,16 +38,18 @@ function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: numbe
60 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length) 38 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length)
61} 39}
62 40
63async function flushAndRunServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebtorrent = true) { 41async function createSingleServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebtorrent = true) {
64 const strategies: any[] = [] 42 const strategies: any[] = []
65 43
66 if (strategy !== null) { 44 if (strategy !== null) {
67 strategies.push( 45 strategies.push(
68 immutableAssign({ 46 {
69 min_lifetime: '1 hour', 47 min_lifetime: '1 hour',
70 strategy: strategy, 48 strategy: strategy,
71 size: '400KB' 49 size: '400KB',
72 }, additionalParams) 50
51 ...additionalParams
52 }
73 ) 53 )
74 } 54 }
75 55
@@ -90,17 +70,17 @@ async function flushAndRunServers (strategy: VideoRedundancyStrategy | null, add
90 } 70 }
91 } 71 }
92 72
93 servers = await flushAndRunMultipleServers(3, config) 73 servers = await createMultipleServers(3, config)
94 74
95 // Get the access tokens 75 // Get the access tokens
96 await setAccessTokensToServers(servers) 76 await setAccessTokensToServers(servers)
97 77
98 { 78 {
99 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 server 2' }) 79 const { uuid, id } = await servers[1].videos.upload({ attributes: { name: 'video 1 server 2' } })
100 video1Server2UUID = res.body.video.uuid 80 video1Server2UUID = uuid
101 video1Server2Id = res.body.video.id 81 video1Server2Id = id
102 82
103 await viewVideo(servers[1].url, video1Server2UUID) 83 await servers[1].videos.view({ id: video1Server2UUID })
104 } 84 }
105 85
106 await waitJobs(servers) 86 await waitJobs(servers)
@@ -124,9 +104,8 @@ async function check1WebSeed (videoUUID?: string) {
124 104
125 for (const server of servers) { 105 for (const server of servers) {
126 // With token to avoid issues with video follow constraints 106 // With token to avoid issues with video follow constraints
127 const res = await getVideoWithToken(server.url, server.accessToken, videoUUID) 107 const video = await server.videos.getWithToken({ id: videoUUID })
128 108
129 const video: VideoDetails = res.body
130 for (const f of video.files) { 109 for (const f of video.files) {
131 checkMagnetWebseeds(f, webseeds, server) 110 checkMagnetWebseeds(f, webseeds, server)
132 } 111 }
@@ -142,22 +121,20 @@ async function check2Webseeds (videoUUID?: string) {
142 ] 121 ]
143 122
144 for (const server of servers) { 123 for (const server of servers) {
145 const res = await getVideo(server.url, videoUUID) 124 const video = await server.videos.get({ id: videoUUID })
146
147 const video: VideoDetails = res.body
148 125
149 for (const file of video.files) { 126 for (const file of video.files) {
150 checkMagnetWebseeds(file, webseeds, server) 127 checkMagnetWebseeds(file, webseeds, server)
151 128
152 await makeGetRequest({ 129 await makeGetRequest({
153 url: servers[0].url, 130 url: servers[0].url,
154 statusCodeExpected: HttpStatusCode.OK_200, 131 expectedStatus: HttpStatusCode.OK_200,
155 path: '/static/redundancy/' + `${videoUUID}-${file.resolution.id}.mp4`, 132 path: '/static/redundancy/' + `${videoUUID}-${file.resolution.id}.mp4`,
156 contentType: null 133 contentType: null
157 }) 134 })
158 await makeGetRequest({ 135 await makeGetRequest({
159 url: servers[1].url, 136 url: servers[1].url,
160 statusCodeExpected: HttpStatusCode.OK_200, 137 expectedStatus: HttpStatusCode.OK_200,
161 path: `/static/webseed/${videoUUID}-${file.resolution.id}.mp4`, 138 path: `/static/webseed/${videoUUID}-${file.resolution.id}.mp4`,
162 contentType: null 139 contentType: null
163 }) 140 })
@@ -184,8 +161,7 @@ async function check0PlaylistRedundancies (videoUUID?: string) {
184 161
185 for (const server of servers) { 162 for (const server of servers) {
186 // With token to avoid issues with video follow constraints 163 // With token to avoid issues with video follow constraints
187 const res = await getVideoWithToken(server.url, server.accessToken, videoUUID) 164 const video = await server.videos.getWithToken({ id: videoUUID })
188 const video: VideoDetails = res.body
189 165
190 expect(video.streamingPlaylists).to.be.an('array') 166 expect(video.streamingPlaylists).to.be.an('array')
191 expect(video.streamingPlaylists).to.have.lengthOf(1) 167 expect(video.streamingPlaylists).to.have.lengthOf(1)
@@ -197,8 +173,7 @@ async function check1PlaylistRedundancies (videoUUID?: string) {
197 if (!videoUUID) videoUUID = video1Server2UUID 173 if (!videoUUID) videoUUID = video1Server2UUID
198 174
199 for (const server of servers) { 175 for (const server of servers) {
200 const res = await getVideo(server.url, videoUUID) 176 const video = await server.videos.get({ id: videoUUID })
201 const video: VideoDetails = res.body
202 177
203 expect(video.streamingPlaylists).to.have.lengthOf(1) 178 expect(video.streamingPlaylists).to.have.lengthOf(1)
204 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(1) 179 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(1)
@@ -211,11 +186,11 @@ async function check1PlaylistRedundancies (videoUUID?: string) {
211 const baseUrlPlaylist = servers[1].url + '/static/streaming-playlists/hls' 186 const baseUrlPlaylist = servers[1].url + '/static/streaming-playlists/hls'
212 const baseUrlSegment = servers[0].url + '/static/redundancy/hls' 187 const baseUrlSegment = servers[0].url + '/static/redundancy/hls'
213 188
214 const res = await getVideo(servers[0].url, videoUUID) 189 const video = await servers[0].videos.get({ id: videoUUID })
215 const hlsPlaylist = (res.body as VideoDetails).streamingPlaylists[0] 190 const hlsPlaylist = video.streamingPlaylists[0]
216 191
217 for (const resolution of [ 240, 360, 480, 720 ]) { 192 for (const resolution of [ 240, 360, 480, 720 ]) {
218 await checkSegmentHash(baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist) 193 await checkSegmentHash({ server: servers[1], baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist })
219 } 194 }
220 195
221 const directories = [ 196 const directories = [
@@ -244,9 +219,7 @@ async function checkStatsGlobal (strategy: VideoRedundancyStrategyWithManual) {
244 statsLength = 2 219 statsLength = 2
245 } 220 }
246 221
247 const res = await getStats(servers[0].url) 222 const data = await servers[0].stats.get()
248 const data: ServerStats = res.body
249
250 expect(data.videosRedundancy).to.have.lengthOf(statsLength) 223 expect(data.videosRedundancy).to.have.lengthOf(statsLength)
251 224
252 const stat = data.videosRedundancy[0] 225 const stat = data.videosRedundancy[0]
@@ -272,14 +245,20 @@ async function checkStatsWithoutRedundancy (strategy: VideoRedundancyStrategyWit
272 expect(stat.totalVideos).to.equal(0) 245 expect(stat.totalVideos).to.equal(0)
273} 246}
274 247
275async function enableRedundancyOnServer1 () { 248async function findServerFollows () {
276 await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, true) 249 const body = await servers[0].follows.getFollowings({ start: 0, count: 5, sort: '-createdAt' })
277 250 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}`) 251 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}`) 252 const server3 = follows.find(f => f.following.host === `localhost:${servers[2].port}`)
282 253
254 return { server2, server3 }
255}
256
257async function enableRedundancyOnServer1 () {
258 await servers[0].redundancy.updateRedundancy({ host: servers[1].host, redundancyAllowed: true })
259
260 const { server2, server3 } = await findServerFollows()
261
283 expect(server3).to.not.be.undefined 262 expect(server3).to.not.be.undefined
284 expect(server3.following.hostRedundancyAllowed).to.be.false 263 expect(server3.following.hostRedundancyAllowed).to.be.false
285 264
@@ -288,12 +267,9 @@ async function enableRedundancyOnServer1 () {
288} 267}
289 268
290async function disableRedundancyOnServer1 () { 269async function disableRedundancyOnServer1 () {
291 await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, false) 270 await servers[0].redundancy.updateRedundancy({ host: servers[1].host, redundancyAllowed: false })
292 271
293 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: '-createdAt' }) 272 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 273
298 expect(server3).to.not.be.undefined 274 expect(server3).to.not.be.undefined
299 expect(server3.following.hostRedundancyAllowed).to.be.false 275 expect(server3.following.hostRedundancyAllowed).to.be.false
@@ -310,7 +286,7 @@ describe('Test videos redundancy', function () {
310 before(function () { 286 before(function () {
311 this.timeout(120000) 287 this.timeout(120000)
312 288
313 return flushAndRunServers(strategy) 289 return createSingleServers(strategy)
314 }) 290 })
315 291
316 it('Should have 1 webseed on the first video', async function () { 292 it('Should have 1 webseed on the first video', async function () {
@@ -327,7 +303,7 @@ describe('Test videos redundancy', function () {
327 this.timeout(80000) 303 this.timeout(80000)
328 304
329 await waitJobs(servers) 305 await waitJobs(servers)
330 await waitUntilLog(servers[0], 'Duplicated ', 5) 306 await servers[0].servers.waitUntilLog('Duplicated ', 5)
331 await waitJobs(servers) 307 await waitJobs(servers)
332 308
333 await check2Webseeds() 309 await check2Webseeds()
@@ -346,7 +322,7 @@ describe('Test videos redundancy', function () {
346 await check1WebSeed() 322 await check1WebSeed()
347 await check0PlaylistRedundancies() 323 await check0PlaylistRedundancies()
348 324
349 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].internalServerNumber, [ 'videos', join('playlists', 'hls') ]) 325 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0], [ 'videos', join('playlists', 'hls') ])
350 }) 326 })
351 327
352 after(async function () { 328 after(async function () {
@@ -360,7 +336,7 @@ describe('Test videos redundancy', function () {
360 before(function () { 336 before(function () {
361 this.timeout(120000) 337 this.timeout(120000)
362 338
363 return flushAndRunServers(strategy) 339 return createSingleServers(strategy)
364 }) 340 })
365 341
366 it('Should have 1 webseed on the first video', async function () { 342 it('Should have 1 webseed on the first video', async function () {
@@ -377,7 +353,7 @@ describe('Test videos redundancy', function () {
377 this.timeout(80000) 353 this.timeout(80000)
378 354
379 await waitJobs(servers) 355 await waitJobs(servers)
380 await waitUntilLog(servers[0], 'Duplicated ', 5) 356 await servers[0].servers.waitUntilLog('Duplicated ', 5)
381 await waitJobs(servers) 357 await waitJobs(servers)
382 358
383 await check2Webseeds() 359 await check2Webseeds()
@@ -388,7 +364,7 @@ describe('Test videos redundancy', function () {
388 it('Should unfollow on server 1 and remove duplicated videos', async function () { 364 it('Should unfollow on server 1 and remove duplicated videos', async function () {
389 this.timeout(80000) 365 this.timeout(80000)
390 366
391 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 367 await servers[0].follows.unfollow({ target: servers[1] })
392 368
393 await waitJobs(servers) 369 await waitJobs(servers)
394 await wait(5000) 370 await wait(5000)
@@ -396,7 +372,7 @@ describe('Test videos redundancy', function () {
396 await check1WebSeed() 372 await check1WebSeed()
397 await check0PlaylistRedundancies() 373 await check0PlaylistRedundancies()
398 374
399 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].internalServerNumber, [ 'videos' ]) 375 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0], [ 'videos' ])
400 }) 376 })
401 377
402 after(async function () { 378 after(async function () {
@@ -410,7 +386,7 @@ describe('Test videos redundancy', function () {
410 before(function () { 386 before(function () {
411 this.timeout(120000) 387 this.timeout(120000)
412 388
413 return flushAndRunServers(strategy, { min_views: 3 }) 389 return createSingleServers(strategy, { min_views: 3 })
414 }) 390 })
415 391
416 it('Should have 1 webseed on the first video', async function () { 392 it('Should have 1 webseed on the first video', async function () {
@@ -438,8 +414,8 @@ describe('Test videos redundancy', function () {
438 it('Should view 2 times the first video to have > min_views config', async function () { 414 it('Should view 2 times the first video to have > min_views config', async function () {
439 this.timeout(80000) 415 this.timeout(80000)
440 416
441 await viewVideo(servers[0].url, video1Server2UUID) 417 await servers[0].videos.view({ id: video1Server2UUID })
442 await viewVideo(servers[2].url, video1Server2UUID) 418 await servers[2].videos.view({ id: video1Server2UUID })
443 419
444 await wait(10000) 420 await wait(10000)
445 await waitJobs(servers) 421 await waitJobs(servers)
@@ -449,7 +425,7 @@ describe('Test videos redundancy', function () {
449 this.timeout(80000) 425 this.timeout(80000)
450 426
451 await waitJobs(servers) 427 await waitJobs(servers)
452 await waitUntilLog(servers[0], 'Duplicated ', 5) 428 await servers[0].servers.waitUntilLog('Duplicated ', 5)
453 await waitJobs(servers) 429 await waitJobs(servers)
454 430
455 await check2Webseeds() 431 await check2Webseeds()
@@ -460,12 +436,12 @@ describe('Test videos redundancy', function () {
460 it('Should remove the video and the redundancy files', async function () { 436 it('Should remove the video and the redundancy files', async function () {
461 this.timeout(20000) 437 this.timeout(20000)
462 438
463 await removeVideo(servers[1].url, servers[1].accessToken, video1Server2UUID) 439 await servers[1].videos.remove({ id: video1Server2UUID })
464 440
465 await waitJobs(servers) 441 await waitJobs(servers)
466 442
467 for (const server of servers) { 443 for (const server of servers) {
468 await checkVideoFilesWereRemoved(video1Server2UUID, server.internalServerNumber) 444 await checkVideoFilesWereRemoved(video1Server2UUID, server)
469 } 445 }
470 }) 446 })
471 447
@@ -480,7 +456,7 @@ describe('Test videos redundancy', function () {
480 before(async function () { 456 before(async function () {
481 this.timeout(120000) 457 this.timeout(120000)
482 458
483 await flushAndRunServers(strategy, { min_views: 3 }, false) 459 await createSingleServers(strategy, { min_views: 3 }, false)
484 }) 460 })
485 461
486 it('Should have 0 playlist redundancy on the first video', async function () { 462 it('Should have 0 playlist redundancy on the first video', async function () {
@@ -506,14 +482,14 @@ describe('Test videos redundancy', function () {
506 it('Should have 1 redundancy on the first video', async function () { 482 it('Should have 1 redundancy on the first video', async function () {
507 this.timeout(160000) 483 this.timeout(160000)
508 484
509 await viewVideo(servers[0].url, video1Server2UUID) 485 await servers[0].videos.view({ id: video1Server2UUID })
510 await viewVideo(servers[2].url, video1Server2UUID) 486 await servers[2].videos.view({ id: video1Server2UUID })
511 487
512 await wait(10000) 488 await wait(10000)
513 await waitJobs(servers) 489 await waitJobs(servers)
514 490
515 await waitJobs(servers) 491 await waitJobs(servers)
516 await waitUntilLog(servers[0], 'Duplicated ', 1) 492 await servers[0].servers.waitUntilLog('Duplicated ', 1)
517 await waitJobs(servers) 493 await waitJobs(servers)
518 494
519 await check1PlaylistRedundancies() 495 await check1PlaylistRedundancies()
@@ -523,12 +499,12 @@ describe('Test videos redundancy', function () {
523 it('Should remove the video and the redundancy files', async function () { 499 it('Should remove the video and the redundancy files', async function () {
524 this.timeout(20000) 500 this.timeout(20000)
525 501
526 await removeVideo(servers[1].url, servers[1].accessToken, video1Server2UUID) 502 await servers[1].videos.remove({ id: video1Server2UUID })
527 503
528 await waitJobs(servers) 504 await waitJobs(servers)
529 505
530 for (const server of servers) { 506 for (const server of servers) {
531 await checkVideoFilesWereRemoved(video1Server2UUID, server.internalServerNumber) 507 await checkVideoFilesWereRemoved(video1Server2UUID, server)
532 } 508 }
533 }) 509 })
534 510
@@ -541,7 +517,7 @@ describe('Test videos redundancy', function () {
541 before(function () { 517 before(function () {
542 this.timeout(120000) 518 this.timeout(120000)
543 519
544 return flushAndRunServers(null) 520 return createSingleServers(null)
545 }) 521 })
546 522
547 it('Should have 1 webseed on the first video', async function () { 523 it('Should have 1 webseed on the first video', async function () {
@@ -551,18 +527,14 @@ describe('Test videos redundancy', function () {
551 }) 527 })
552 528
553 it('Should create a redundancy on first video', async function () { 529 it('Should create a redundancy on first video', async function () {
554 await addVideoRedundancy({ 530 await servers[0].redundancy.addVideo({ videoId: video1Server2Id })
555 url: servers[0].url,
556 accessToken: servers[0].accessToken,
557 videoId: video1Server2Id
558 })
559 }) 531 })
560 532
561 it('Should have 2 webseeds on the first video', async function () { 533 it('Should have 2 webseeds on the first video', async function () {
562 this.timeout(80000) 534 this.timeout(80000)
563 535
564 await waitJobs(servers) 536 await waitJobs(servers)
565 await waitUntilLog(servers[0], 'Duplicated ', 5) 537 await servers[0].servers.waitUntilLog('Duplicated ', 5)
566 await waitJobs(servers) 538 await waitJobs(servers)
567 539
568 await check2Webseeds() 540 await check2Webseeds()
@@ -573,22 +545,15 @@ describe('Test videos redundancy', function () {
573 it('Should manually remove redundancies on server 1 and remove duplicated videos', async function () { 545 it('Should manually remove redundancies on server 1 and remove duplicated videos', async function () {
574 this.timeout(80000) 546 this.timeout(80000)
575 547
576 const res = await listVideoRedundancies({ 548 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 549
582 const videos = res.body.data as VideoRedundancy[] 550 const videos = body.data
583 expect(videos).to.have.lengthOf(1) 551 expect(videos).to.have.lengthOf(1)
584 552
585 const video = videos[0] 553 const video = videos[0]
554
586 for (const r of video.redundancies.files.concat(video.redundancies.streamingPlaylists)) { 555 for (const r of video.redundancies.files.concat(video.redundancies.streamingPlaylists)) {
587 await removeVideoRedundancy({ 556 await servers[0].redundancy.removeVideo({ redundancyId: r.id })
588 url: servers[0].url,
589 accessToken: servers[0].accessToken,
590 redundancyId: r.id
591 })
592 } 557 }
593 558
594 await waitJobs(servers) 559 await waitJobs(servers)
@@ -597,7 +562,7 @@ describe('Test videos redundancy', function () {
597 await check1WebSeed() 562 await check1WebSeed()
598 await check0PlaylistRedundancies() 563 await check0PlaylistRedundancies()
599 564
600 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].serverNumber, [ 'videos' ]) 565 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0], [ 'videos' ])
601 }) 566 })
602 567
603 after(async function () { 568 after(async function () {
@@ -608,10 +573,9 @@ describe('Test videos redundancy', function () {
608 describe('Test expiration', function () { 573 describe('Test expiration', function () {
609 const strategy = 'recently-added' 574 const strategy = 'recently-added'
610 575
611 async function checkContains (servers: ServerInfo[], str: string) { 576 async function checkContains (servers: PeerTubeServer[], str: string) {
612 for (const server of servers) { 577 for (const server of servers) {
613 const res = await getVideo(server.url, video1Server2UUID) 578 const video = await server.videos.get({ id: video1Server2UUID })
614 const video: VideoDetails = res.body
615 579
616 for (const f of video.files) { 580 for (const f of video.files) {
617 expect(f.magnetUri).to.contain(str) 581 expect(f.magnetUri).to.contain(str)
@@ -619,10 +583,9 @@ describe('Test videos redundancy', function () {
619 } 583 }
620 } 584 }
621 585
622 async function checkNotContains (servers: ServerInfo[], str: string) { 586 async function checkNotContains (servers: PeerTubeServer[], str: string) {
623 for (const server of servers) { 587 for (const server of servers) {
624 const res = await getVideo(server.url, video1Server2UUID) 588 const video = await server.videos.get({ id: video1Server2UUID })
625 const video: VideoDetails = res.body
626 589
627 for (const f of video.files) { 590 for (const f of video.files) {
628 expect(f.magnetUri).to.not.contain(str) 591 expect(f.magnetUri).to.not.contain(str)
@@ -633,7 +596,7 @@ describe('Test videos redundancy', function () {
633 before(async function () { 596 before(async function () {
634 this.timeout(120000) 597 this.timeout(120000)
635 598
636 await flushAndRunServers(strategy, { min_lifetime: '7 seconds', min_views: 0 }) 599 await createSingleServers(strategy, { min_lifetime: '7 seconds', min_views: 0 })
637 600
638 await enableRedundancyOnServer1() 601 await enableRedundancyOnServer1()
639 }) 602 })
@@ -656,7 +619,7 @@ describe('Test videos redundancy', function () {
656 it('Should stop server 1 and expire video redundancy', async function () { 619 it('Should stop server 1 and expire video redundancy', async function () {
657 this.timeout(80000) 620 this.timeout(80000)
658 621
659 killallServers([ servers[0] ]) 622 await killallServers([ servers[0] ])
660 623
661 await wait(15000) 624 await wait(15000)
662 625
@@ -675,25 +638,25 @@ describe('Test videos redundancy', function () {
675 before(async function () { 638 before(async function () {
676 this.timeout(120000) 639 this.timeout(120000)
677 640
678 await flushAndRunServers(strategy, { min_lifetime: '7 seconds', min_views: 0 }) 641 await createSingleServers(strategy, { min_lifetime: '7 seconds', min_views: 0 })
679 642
680 await enableRedundancyOnServer1() 643 await enableRedundancyOnServer1()
681 644
682 await waitJobs(servers) 645 await waitJobs(servers)
683 await waitUntilLog(servers[0], 'Duplicated ', 5) 646 await servers[0].servers.waitUntilLog('Duplicated ', 5)
684 await waitJobs(servers) 647 await waitJobs(servers)
685 648
686 await check2Webseeds(video1Server2UUID) 649 await check2Webseeds(video1Server2UUID)
687 await check1PlaylistRedundancies(video1Server2UUID) 650 await check1PlaylistRedundancies(video1Server2UUID)
688 await checkStatsWith1Redundancy(strategy) 651 await checkStatsWith1Redundancy(strategy)
689 652
690 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 2 server 2', privacy: VideoPrivacy.PRIVATE }) 653 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 2 server 2', privacy: VideoPrivacy.PRIVATE } })
691 video2Server2UUID = res.body.video.uuid 654 video2Server2UUID = uuid
692 655
693 // Wait transcoding before federation 656 // Wait transcoding before federation
694 await waitJobs(servers) 657 await waitJobs(servers)
695 658
696 await updateVideo(servers[1].url, servers[1].accessToken, video2Server2UUID, { privacy: VideoPrivacy.PUBLIC }) 659 await servers[1].videos.update({ id: video2Server2UUID, attributes: { privacy: VideoPrivacy.PUBLIC } })
697 }) 660 })
698 661
699 it('Should cache video 2 webseeds on the first video', async function () { 662 it('Should cache video 2 webseeds on the first video', async function () {
@@ -725,8 +688,8 @@ describe('Test videos redundancy', function () {
725 688
726 await waitJobs(servers) 689 await waitJobs(servers)
727 690
728 killallServers([ servers[0] ]) 691 await killallServers([ servers[0] ])
729 await reRunServer(servers[0], { 692 await servers[0].run({
730 redundancy: { 693 redundancy: {
731 videos: { 694 videos: {
732 check_interval: '1 second', 695 check_interval: '1 second',
@@ -737,7 +700,7 @@ describe('Test videos redundancy', function () {
737 700
738 await waitJobs(servers) 701 await waitJobs(servers)
739 702
740 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].internalServerNumber, [ join('redundancy', 'hls') ]) 703 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0], [ join('redundancy', 'hls') ])
741 }) 704 })
742 705
743 after(async function () { 706 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..4da2d0ece 100644
--- a/server/tests/api/search/search-channels.ts
+++ b/server/tests/api/search/search-channels.ts
@@ -2,44 +2,39 @@
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' 5import { cleanupTests, createSingleServer, PeerTubeServer, SearchCommand, setAccessTokensToServers } from '@shared/extra-utils'
6import {
7 addVideoChannel,
8 cleanupTests,
9 createUser,
10 flushAndRunServer,
11 ServerInfo,
12 setAccessTokensToServers
13} from '../../../../shared/extra-utils'
14import { VideoChannel } from '@shared/models' 6import { VideoChannel } from '@shared/models'
15 7
16const expect = chai.expect 8const expect = chai.expect
17 9
18describe('Test channels search', function () { 10describe('Test channels search', function () {
19 let server: ServerInfo = null 11 let server: PeerTubeServer = null
12 let command: SearchCommand
20 13
21 before(async function () { 14 before(async function () {
22 this.timeout(30000) 15 this.timeout(30000)
23 16
24 server = await flushAndRunServer(1) 17 server = await createSingleServer(1)
25 18
26 await setAccessTokensToServers([ server ]) 19 await setAccessTokensToServers([ server ])
27 20
28 { 21 {
29 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user1', password: 'password' }) 22 await server.users.create({ username: 'user1', password: 'password' })
30 const channel = { 23 const channel = {
31 name: 'squall_channel', 24 name: 'squall_channel',
32 displayName: 'Squall channel' 25 displayName: 'Squall channel'
33 } 26 }
34 await addVideoChannel(server.url, server.accessToken, channel) 27 await server.channels.create({ attributes: channel })
35 } 28 }
29
30 command = server.search
36 }) 31 })
37 32
38 it('Should make a simple search and not have results', async function () { 33 it('Should make a simple search and not have results', async function () {
39 const res = await searchVideoChannel(server.url, 'abc') 34 const body = await command.searchChannels({ search: 'abc' })
40 35
41 expect(res.body.total).to.equal(0) 36 expect(body.total).to.equal(0)
42 expect(res.body.data).to.have.lengthOf(0) 37 expect(body.data).to.have.lengthOf(0)
43 }) 38 })
44 39
45 it('Should make a search and have results', async function () { 40 it('Should make a search and have results', async function () {
@@ -49,11 +44,11 @@ describe('Test channels search', function () {
49 start: 0, 44 start: 0,
50 count: 1 45 count: 1
51 } 46 }
52 const res = await advancedVideoChannelSearch(server.url, search) 47 const body = await command.advancedChannelSearch({ search })
53 expect(res.body.total).to.equal(1) 48 expect(body.total).to.equal(1)
54 expect(res.body.data).to.have.lengthOf(1) 49 expect(body.data).to.have.lengthOf(1)
55 50
56 const channel: VideoChannel = res.body.data[0] 51 const channel: VideoChannel = body.data[0]
57 expect(channel.name).to.equal('squall_channel') 52 expect(channel.name).to.equal('squall_channel')
58 expect(channel.displayName).to.equal('Squall channel') 53 expect(channel.displayName).to.equal('Squall channel')
59 } 54 }
@@ -65,11 +60,9 @@ describe('Test channels search', function () {
65 count: 1 60 count: 1
66 } 61 }
67 62
68 const res = await advancedVideoChannelSearch(server.url, search) 63 const body = await command.advancedChannelSearch({ search })
69 64 expect(body.total).to.equal(1)
70 expect(res.body.total).to.equal(1) 65 expect(body.data).to.have.lengthOf(0)
71
72 expect(res.body.data).to.have.lengthOf(0)
73 } 66 }
74 }) 67 })
75 68
diff --git a/server/tests/api/search/search-index.ts b/server/tests/api/search/search-index.ts
index 00f79232a..feb35411f 100644
--- a/server/tests/api/search/search-index.ts
+++ b/server/tests/api/search/search-index.ts
@@ -2,36 +2,27 @@
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' 6import { BooleanBothQuery, VideoPlaylistPrivacy, VideoPlaylistType, VideosSearchQuery } from '@shared/models'
7import {
8 advancedVideoPlaylistSearch,
9 advancedVideosSearch,
10 cleanupTests,
11 flushAndRunServer,
12 immutableAssign,
13 searchVideo,
14 searchVideoPlaylists,
15 ServerInfo,
16 setAccessTokensToServers,
17 updateCustomSubConfig,
18 uploadVideo
19} from '../../../../shared/extra-utils'
20 7
21const expect = chai.expect 8const expect = chai.expect
22 9
23describe('Test videos search', function () { 10describe('Test videos search', function () {
24 let server: ServerInfo = null
25 const localVideoName = 'local video' + new Date().toISOString() 11 const localVideoName = 'local video' + new Date().toISOString()
26 12
13 let server: PeerTubeServer = null
14 let command: SearchCommand
15
27 before(async function () { 16 before(async function () {
28 this.timeout(30000) 17 this.timeout(30000)
29 18
30 server = await flushAndRunServer(1) 19 server = await createSingleServer(1)
31 20
32 await setAccessTokensToServers([ server ]) 21 await setAccessTokensToServers([ server ])
33 22
34 await uploadVideo(server.url, server.accessToken, { name: localVideoName }) 23 await server.videos.upload({ attributes: { name: localVideoName } })
24
25 command = server.search
35 }) 26 })
36 27
37 describe('Default search', async function () { 28 describe('Default search', async function () {
@@ -39,101 +30,107 @@ describe('Test videos search', function () {
39 it('Should make a local videos search by default', async function () { 30 it('Should make a local videos search by default', async function () {
40 this.timeout(10000) 31 this.timeout(10000)
41 32
42 await updateCustomSubConfig(server.url, server.accessToken, { 33 await server.config.updateCustomSubConfig({
43 search: { 34 newConfig: {
44 searchIndex: { 35 search: {
45 enabled: true, 36 searchIndex: {
46 isDefaultSearch: false, 37 enabled: true,
47 disableLocalSearch: false 38 isDefaultSearch: false,
39 disableLocalSearch: false
40 }
48 } 41 }
49 } 42 }
50 }) 43 })
51 44
52 const res = await searchVideo(server.url, 'local video') 45 const body = await command.searchVideos({ search: 'local video' })
53 46
54 expect(res.body.total).to.equal(1) 47 expect(body.total).to.equal(1)
55 expect(res.body.data[0].name).to.equal(localVideoName) 48 expect(body.data[0].name).to.equal(localVideoName)
56 }) 49 })
57 50
58 it('Should make a local channels search by default', async function () { 51 it('Should make a local channels search by default', async function () {
59 const res = await searchVideoChannel(server.url, 'root') 52 const body = await command.searchChannels({ search: 'root' })
60 53
61 expect(res.body.total).to.equal(1) 54 expect(body.total).to.equal(1)
62 expect(res.body.data[0].name).to.equal('root_channel') 55 expect(body.data[0].name).to.equal('root_channel')
63 expect(res.body.data[0].host).to.equal('localhost:' + server.port) 56 expect(body.data[0].host).to.equal('localhost:' + server.port)
64 }) 57 })
65 58
66 it('Should make an index videos search by default', async function () { 59 it('Should make an index videos search by default', async function () {
67 await updateCustomSubConfig(server.url, server.accessToken, { 60 await server.config.updateCustomSubConfig({
68 search: { 61 newConfig: {
69 searchIndex: { 62 search: {
70 enabled: true, 63 searchIndex: {
71 isDefaultSearch: true, 64 enabled: true,
72 disableLocalSearch: false 65 isDefaultSearch: true,
66 disableLocalSearch: false
67 }
73 } 68 }
74 } 69 }
75 }) 70 })
76 71
77 const res = await searchVideo(server.url, 'local video') 72 const body = await command.searchVideos({ search: 'local video' })
78 expect(res.body.total).to.be.greaterThan(2) 73 expect(body.total).to.be.greaterThan(2)
79 }) 74 })
80 75
81 it('Should make an index channels search by default', async function () { 76 it('Should make an index channels search by default', async function () {
82 const res = await searchVideoChannel(server.url, 'root') 77 const body = await command.searchChannels({ search: 'root' })
83 expect(res.body.total).to.be.greaterThan(2) 78 expect(body.total).to.be.greaterThan(2)
84 }) 79 })
85 80
86 it('Should make an index videos search if local search is disabled', async function () { 81 it('Should make an index videos search if local search is disabled', async function () {
87 await updateCustomSubConfig(server.url, server.accessToken, { 82 await server.config.updateCustomSubConfig({
88 search: { 83 newConfig: {
89 searchIndex: { 84 search: {
90 enabled: true, 85 searchIndex: {
91 isDefaultSearch: false, 86 enabled: true,
92 disableLocalSearch: true 87 isDefaultSearch: false,
88 disableLocalSearch: true
89 }
93 } 90 }
94 } 91 }
95 }) 92 })
96 93
97 const res = await searchVideo(server.url, 'local video') 94 const body = await command.searchVideos({ search: 'local video' })
98 expect(res.body.total).to.be.greaterThan(2) 95 expect(body.total).to.be.greaterThan(2)
99 }) 96 })
100 97
101 it('Should make an index channels search if local search is disabled', async function () { 98 it('Should make an index channels search if local search is disabled', async function () {
102 const res = await searchVideoChannel(server.url, 'root') 99 const body = await command.searchChannels({ search: 'root' })
103 expect(res.body.total).to.be.greaterThan(2) 100 expect(body.total).to.be.greaterThan(2)
104 }) 101 })
105 }) 102 })
106 103
107 describe('Videos search', async function () { 104 describe('Videos search', async function () {
108 105
109 it('Should make a simple search and not have results', async function () { 106 it('Should make a simple search and not have results', async function () {
110 const res = await searchVideo(server.url, 'djidane'.repeat(50)) 107 const body = await command.searchVideos({ search: 'djidane'.repeat(50) })
111 108
112 expect(res.body.total).to.equal(0) 109 expect(body.total).to.equal(0)
113 expect(res.body.data).to.have.lengthOf(0) 110 expect(body.data).to.have.lengthOf(0)
114 }) 111 })
115 112
116 it('Should make a simple search and have results', async function () { 113 it('Should make a simple search and have results', async function () {
117 const res = await searchVideo(server.url, 'What is PeerTube') 114 const body = await command.searchVideos({ search: 'What is PeerTube' })
118 115
119 expect(res.body.total).to.be.greaterThan(1) 116 expect(body.total).to.be.greaterThan(1)
120 }) 117 })
121 118
122 it('Should make a complex search', async function () { 119 it('Should make a complex search', async function () {
123 120
124 async function check (search: VideosSearchQuery, exists = true) { 121 async function check (search: VideosSearchQuery, exists = true) {
125 const res = await advancedVideosSearch(server.url, search) 122 const body = await command.advancedVideoSearch({ search })
126 123
127 if (exists === false) { 124 if (exists === false) {
128 expect(res.body.total).to.equal(0) 125 expect(body.total).to.equal(0)
129 expect(res.body.data).to.have.lengthOf(0) 126 expect(body.data).to.have.lengthOf(0)
130 return 127 return
131 } 128 }
132 129
133 expect(res.body.total).to.equal(1) 130 expect(body.total).to.equal(1)
134 expect(res.body.data).to.have.lengthOf(1) 131 expect(body.data).to.have.lengthOf(1)
135 132
136 const video: Video = res.body.data[0] 133 const video = body.data[0]
137 134
138 expect(video.name).to.equal('What is PeerTube?') 135 expect(video.name).to.equal('What is PeerTube?')
139 expect(video.category.label).to.equal('Science & Technology') 136 expect(video.category.label).to.equal('Science & Technology')
@@ -169,32 +166,32 @@ describe('Test videos search', function () {
169 } 166 }
170 167
171 { 168 {
172 const search = immutableAssign(baseSearch, { startDate: '2018-10-01T10:54:46.396Z' }) 169 const search = { ...baseSearch, startDate: '2018-10-01T10:54:46.396Z' }
173 await check(search, false) 170 await check(search, false)
174 } 171 }
175 172
176 { 173 {
177 const search = immutableAssign(baseSearch, { tagsAllOf: [ 'toto', 'framasoft' ] }) 174 const search = { ...baseSearch, tagsAllOf: [ 'toto', 'framasoft' ] }
178 await check(search, false) 175 await check(search, false)
179 } 176 }
180 177
181 { 178 {
182 const search = immutableAssign(baseSearch, { durationMin: 2000 }) 179 const search = { ...baseSearch, durationMin: 2000 }
183 await check(search, false) 180 await check(search, false)
184 } 181 }
185 182
186 { 183 {
187 const search = immutableAssign(baseSearch, { nsfw: 'true' }) 184 const search = { ...baseSearch, nsfw: 'true' as BooleanBothQuery }
188 await check(search, false) 185 await check(search, false)
189 } 186 }
190 187
191 { 188 {
192 const search = immutableAssign(baseSearch, { nsfw: 'false' }) 189 const search = { ...baseSearch, nsfw: 'false' as BooleanBothQuery }
193 await check(search, true) 190 await check(search, true)
194 } 191 }
195 192
196 { 193 {
197 const search = immutableAssign(baseSearch, { nsfw: 'both' }) 194 const search = { ...baseSearch, nsfw: 'both' as BooleanBothQuery }
198 await check(search, true) 195 await check(search, true)
199 } 196 }
200 }) 197 })
@@ -206,37 +203,44 @@ describe('Test videos search', function () {
206 count: 5 203 count: 5
207 } 204 }
208 205
209 const res = await advancedVideosSearch(server.url, search) 206 const body = await command.advancedVideoSearch({ search })
210 207
211 expect(res.body.total).to.be.greaterThan(5) 208 expect(body.total).to.be.greaterThan(5)
212 expect(res.body.data).to.have.lengthOf(5) 209 expect(body.data).to.have.lengthOf(5)
213 }) 210 })
214 211
215 it('Should use the nsfw instance policy as default', async function () { 212 it('Should use the nsfw instance policy as default', async function () {
216 let nsfwUUID: string 213 let nsfwUUID: string
217 214
218 { 215 {
219 await updateCustomSubConfig(server.url, server.accessToken, { instance: { defaultNSFWPolicy: 'display' } }) 216 await server.config.updateCustomSubConfig({
217 newConfig: {
218 instance: { defaultNSFWPolicy: 'display' }
219 }
220 })
220 221
221 const res = await searchVideo(server.url, 'NSFW search index', '-match') 222 const body = await command.searchVideos({ search: 'NSFW search index', sort: '-match' })
222 const video = res.body.data[0] as Video 223 expect(body.data).to.have.length.greaterThan(0)
223 224
224 expect(res.body.data).to.have.length.greaterThan(0) 225 const video = body.data[0]
225 expect(video.nsfw).to.be.true 226 expect(video.nsfw).to.be.true
226 227
227 nsfwUUID = video.uuid 228 nsfwUUID = video.uuid
228 } 229 }
229 230
230 { 231 {
231 await updateCustomSubConfig(server.url, server.accessToken, { instance: { defaultNSFWPolicy: 'do_not_list' } }) 232 await server.config.updateCustomSubConfig({
233 newConfig: {
234 instance: { defaultNSFWPolicy: 'do_not_list' }
235 }
236 })
232 237
233 const res = await searchVideo(server.url, 'NSFW search index', '-match') 238 const body = await command.searchVideos({ search: 'NSFW search index', sort: '-match' })
234 239
235 try { 240 try {
236 expect(res.body.data).to.have.lengthOf(0) 241 expect(body.data).to.have.lengthOf(0)
237 } catch (err) { 242 } catch {
238 // 243 const video = body.data[0]
239 const video = res.body.data[0] as Video
240 244
241 expect(video.uuid).not.equal(nsfwUUID) 245 expect(video.uuid).not.equal(nsfwUUID)
242 } 246 }
@@ -247,19 +251,19 @@ describe('Test videos search', function () {
247 describe('Channels search', async function () { 251 describe('Channels search', async function () {
248 252
249 it('Should make a simple search and not have results', async function () { 253 it('Should make a simple search and not have results', async function () {
250 const res = await searchVideoChannel(server.url, 'a'.repeat(500)) 254 const body = await command.searchChannels({ search: 'a'.repeat(500) })
251 255
252 expect(res.body.total).to.equal(0) 256 expect(body.total).to.equal(0)
253 expect(res.body.data).to.have.lengthOf(0) 257 expect(body.data).to.have.lengthOf(0)
254 }) 258 })
255 259
256 it('Should make a search and have results', async function () { 260 it('Should make a search and have results', async function () {
257 const res = await advancedVideoChannelSearch(server.url, { search: 'Framasoft', sort: 'createdAt' }) 261 const body = await command.advancedChannelSearch({ search: { search: 'Framasoft', sort: 'createdAt' } })
258 262
259 expect(res.body.total).to.be.greaterThan(0) 263 expect(body.total).to.be.greaterThan(0)
260 expect(res.body.data).to.have.length.greaterThan(0) 264 expect(body.data).to.have.length.greaterThan(0)
261 265
262 const videoChannel: VideoChannel = res.body.data[0] 266 const videoChannel = body.data[0]
263 expect(videoChannel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8') 267 expect(videoChannel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8')
264 expect(videoChannel.host).to.equal('framatube.org') 268 expect(videoChannel.host).to.equal('framatube.org')
265 expect(videoChannel.avatar).to.exist 269 expect(videoChannel.avatar).to.exist
@@ -272,29 +276,29 @@ describe('Test videos search', function () {
272 }) 276 })
273 277
274 it('Should have a correct pagination', async function () { 278 it('Should have a correct pagination', async function () {
275 const res = await advancedVideoChannelSearch(server.url, { search: 'root', start: 0, count: 2 }) 279 const body = await command.advancedChannelSearch({ search: { search: 'root', start: 0, count: 2 } })
276 280
277 expect(res.body.total).to.be.greaterThan(2) 281 expect(body.total).to.be.greaterThan(2)
278 expect(res.body.data).to.have.lengthOf(2) 282 expect(body.data).to.have.lengthOf(2)
279 }) 283 })
280 }) 284 })
281 285
282 describe('Playlists search', async function () { 286 describe('Playlists search', async function () {
283 287
284 it('Should make a simple search and not have results', async function () { 288 it('Should make a simple search and not have results', async function () {
285 const res = await searchVideoPlaylists(server.url, 'a'.repeat(500)) 289 const body = await command.searchPlaylists({ search: 'a'.repeat(500) })
286 290
287 expect(res.body.total).to.equal(0) 291 expect(body.total).to.equal(0)
288 expect(res.body.data).to.have.lengthOf(0) 292 expect(body.data).to.have.lengthOf(0)
289 }) 293 })
290 294
291 it('Should make a search and have results', async function () { 295 it('Should make a search and have results', async function () {
292 const res = await advancedVideoPlaylistSearch(server.url, { search: 'E2E playlist', sort: '-match' }) 296 const body = await command.advancedPlaylistSearch({ search: { search: 'E2E playlist', sort: '-match' } })
293 297
294 expect(res.body.total).to.be.greaterThan(0) 298 expect(body.total).to.be.greaterThan(0)
295 expect(res.body.data).to.have.length.greaterThan(0) 299 expect(body.data).to.have.length.greaterThan(0)
296 300
297 const videoPlaylist: VideoPlaylist = res.body.data[0] 301 const videoPlaylist = body.data[0]
298 302
299 expect(videoPlaylist.url).to.equal('https://peertube2.cpy.re/videos/watch/playlist/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a') 303 expect(videoPlaylist.url).to.equal('https://peertube2.cpy.re/videos/watch/playlist/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a')
300 expect(videoPlaylist.thumbnailUrl).to.exist 304 expect(videoPlaylist.thumbnailUrl).to.exist
@@ -322,10 +326,10 @@ describe('Test videos search', function () {
322 }) 326 })
323 327
324 it('Should have a correct pagination', async function () { 328 it('Should have a correct pagination', async function () {
325 const res = await advancedVideoChannelSearch(server.url, { search: 'root', start: 0, count: 2 }) 329 const body = await command.advancedChannelSearch({ search: { search: 'root', start: 0, count: 2 } })
326 330
327 expect(res.body.total).to.be.greaterThan(2) 331 expect(body.total).to.be.greaterThan(2)
328 expect(res.body.data).to.have.lengthOf(2) 332 expect(body.data).to.have.lengthOf(2)
329 }) 333 })
330 }) 334 })
331 335
diff --git a/server/tests/api/search/search-playlists.ts b/server/tests/api/search/search-playlists.ts
index ab17d55e9..22e9b8fca 100644
--- a/server/tests/api/search/search-playlists.ts
+++ b/server/tests/api/search/search-playlists.ts
@@ -2,82 +2,71 @@
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 PeerTubeServer,
12 searchVideoPlaylists, 9 SearchCommand,
13 ServerInfo,
14 setAccessTokensToServers, 10 setAccessTokensToServers,
15 setDefaultVideoChannel, 11 setDefaultVideoChannel
16 uploadVideoAndGetId 12} from '@shared/extra-utils'
17} from '../../../../shared/extra-utils' 13import { VideoPlaylistPrivacy } from '@shared/models'
18 14
19const expect = chai.expect 15const expect = chai.expect
20 16
21describe('Test playlists search', function () { 17describe('Test playlists search', function () {
22 let server: ServerInfo = null 18 let server: PeerTubeServer = null
19 let command: SearchCommand
23 20
24 before(async function () { 21 before(async function () {
25 this.timeout(30000) 22 this.timeout(30000)
26 23
27 server = await flushAndRunServer(1) 24 server = await createSingleServer(1)
28 25
29 await setAccessTokensToServers([ server ]) 26 await setAccessTokensToServers([ server ])
30 await setDefaultVideoChannel([ server ]) 27 await setDefaultVideoChannel([ server ])
31 28
32 const videoId = (await uploadVideoAndGetId({ server: server, videoName: 'video' })).uuid 29 const videoId = (await server.videos.quickUpload({ name: 'video' })).uuid
33 30
34 { 31 {
35 const attributes = { 32 const attributes = {
36 displayName: 'Dr. Kenzo Tenma hospital videos', 33 displayName: 'Dr. Kenzo Tenma hospital videos',
37 privacy: VideoPlaylistPrivacy.PUBLIC, 34 privacy: VideoPlaylistPrivacy.PUBLIC,
38 videoChannelId: server.videoChannel.id 35 videoChannelId: server.store.channel.id
39 } 36 }
40 const res = await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) 37 const created = await server.playlists.create({ attributes })
41 38
42 await addVideoInPlaylist({ 39 await server.playlists.addElement({ playlistId: created.id, attributes: { videoId } })
43 url: server.url,
44 token: server.accessToken,
45 playlistId: res.body.videoPlaylist.id,
46 elementAttrs: { videoId }
47 })
48 } 40 }
49 41
50 { 42 {
51 const attributes = { 43 const attributes = {
52 displayName: 'Johan & Anna Libert musics', 44 displayName: 'Johan & Anna Libert musics',
53 privacy: VideoPlaylistPrivacy.PUBLIC, 45 privacy: VideoPlaylistPrivacy.PUBLIC,
54 videoChannelId: server.videoChannel.id 46 videoChannelId: server.store.channel.id
55 } 47 }
56 const res = await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) 48 const created = await server.playlists.create({ attributes })
57 49
58 await addVideoInPlaylist({ 50 await server.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 } 51 }
65 52
66 { 53 {
67 const attributes = { 54 const attributes = {
68 displayName: 'Inspector Lunge playlist', 55 displayName: 'Inspector Lunge playlist',
69 privacy: VideoPlaylistPrivacy.PUBLIC, 56 privacy: VideoPlaylistPrivacy.PUBLIC,
70 videoChannelId: server.videoChannel.id 57 videoChannelId: server.store.channel.id
71 } 58 }
72 await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) 59 await server.playlists.create({ attributes })
73 } 60 }
61
62 command = server.search
74 }) 63 })
75 64
76 it('Should make a simple search and not have results', async function () { 65 it('Should make a simple search and not have results', async function () {
77 const res = await searchVideoPlaylists(server.url, 'abc') 66 const body = await command.searchPlaylists({ search: 'abc' })
78 67
79 expect(res.body.total).to.equal(0) 68 expect(body.total).to.equal(0)
80 expect(res.body.data).to.have.lengthOf(0) 69 expect(body.data).to.have.lengthOf(0)
81 }) 70 })
82 71
83 it('Should make a search and have results', async function () { 72 it('Should make a search and have results', async function () {
@@ -87,11 +76,11 @@ describe('Test playlists search', function () {
87 start: 0, 76 start: 0,
88 count: 1 77 count: 1
89 } 78 }
90 const res = await advancedVideoPlaylistSearch(server.url, search) 79 const body = await command.advancedPlaylistSearch({ search })
91 expect(res.body.total).to.equal(1) 80 expect(body.total).to.equal(1)
92 expect(res.body.data).to.have.lengthOf(1) 81 expect(body.data).to.have.lengthOf(1)
93 82
94 const playlist: VideoPlaylist = res.body.data[0] 83 const playlist = body.data[0]
95 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos') 84 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos')
96 expect(playlist.url).to.equal(server.url + '/video-playlists/' + playlist.uuid) 85 expect(playlist.url).to.equal(server.url + '/video-playlists/' + playlist.uuid)
97 } 86 }
@@ -102,11 +91,11 @@ describe('Test playlists search', function () {
102 start: 0, 91 start: 0,
103 count: 1 92 count: 1
104 } 93 }
105 const res = await advancedVideoPlaylistSearch(server.url, search) 94 const body = await command.advancedPlaylistSearch({ search })
106 expect(res.body.total).to.equal(1) 95 expect(body.total).to.equal(1)
107 expect(res.body.data).to.have.lengthOf(1) 96 expect(body.data).to.have.lengthOf(1)
108 97
109 const playlist: VideoPlaylist = res.body.data[0] 98 const playlist = body.data[0]
110 expect(playlist.displayName).to.equal('Johan & Anna Libert musics') 99 expect(playlist.displayName).to.equal('Johan & Anna Libert musics')
111 } 100 }
112 }) 101 })
@@ -117,9 +106,9 @@ describe('Test playlists search', function () {
117 start: 0, 106 start: 0,
118 count: 1 107 count: 1
119 } 108 }
120 const res = await advancedVideoPlaylistSearch(server.url, search) 109 const body = await command.advancedPlaylistSearch({ search })
121 expect(res.body.total).to.equal(0) 110 expect(body.total).to.equal(0)
122 expect(res.body.data).to.have.lengthOf(0) 111 expect(body.data).to.have.lengthOf(0)
123 }) 112 })
124 113
125 after(async function () { 114 after(async function () {
diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts
index 5b8907961..965766742 100644
--- a/server/tests/api/search/search-videos.ts
+++ b/server/tests/api/search/search-videos.ts
@@ -2,37 +2,31 @@
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 PeerTubeServer,
11 immutableAssign, 9 SearchCommand,
12 searchVideo,
13 sendRTMPStreamInVideo,
14 ServerInfo,
15 setAccessTokensToServers, 10 setAccessTokensToServers,
16 setDefaultVideoChannel, 11 setDefaultVideoChannel,
17 stopFfmpeg, 12 stopFfmpeg,
18 updateCustomSubConfig, 13 wait
19 uploadVideo, 14} from '@shared/extra-utils'
20 wait, 15import { VideoPrivacy } from '@shared/models'
21 waitUntilLivePublished
22} from '../../../../shared/extra-utils'
23import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions'
24 16
25const expect = chai.expect 17const expect = chai.expect
26 18
27describe('Test videos search', function () { 19describe('Test videos search', function () {
28 let server: ServerInfo = null 20 let server: PeerTubeServer = null
29 let startDate: string 21 let startDate: string
30 let videoUUID: string 22 let videoUUID: string
31 23
24 let command: SearchCommand
25
32 before(async function () { 26 before(async function () {
33 this.timeout(60000) 27 this.timeout(60000)
34 28
35 server = await flushAndRunServer(1) 29 server = await createSingleServer(1)
36 30
37 await setAccessTokensToServers([ server ]) 31 await setAccessTokensToServers([ server ])
38 await setDefaultVideoChannel([ server ]) 32 await setDefaultVideoChannel([ server ])
@@ -46,57 +40,49 @@ describe('Test videos search', function () {
46 nsfw: false, 40 nsfw: false,
47 language: 'fr' 41 language: 'fr'
48 } 42 }
49 await uploadVideo(server.url, server.accessToken, attributes1) 43 await server.videos.upload({ attributes: attributes1 })
50 44
51 const attributes2 = immutableAssign(attributes1, { name: attributes1.name + ' - 2', fixture: 'video_short.mp4' }) 45 const attributes2 = { ...attributes1, name: attributes1.name + ' - 2', fixture: 'video_short.mp4' }
52 await uploadVideo(server.url, server.accessToken, attributes2) 46 await server.videos.upload({ attributes: attributes2 })
53 47
54 { 48 {
55 const attributes3 = immutableAssign(attributes1, { name: attributes1.name + ' - 3', language: undefined }) 49 const attributes3 = { ...attributes1, name: attributes1.name + ' - 3', language: undefined }
56 const res = await uploadVideo(server.url, server.accessToken, attributes3) 50 const { id, uuid } = await server.videos.upload({ attributes: attributes3 })
57 const videoId = res.body.video.id 51 videoUUID = uuid
58 videoUUID = res.body.video.uuid 52
59 53 await server.captions.add({
60 await createVideoCaption({
61 url: server.url,
62 accessToken: server.accessToken,
63 language: 'en', 54 language: 'en',
64 videoId, 55 videoId: id,
65 fixture: 'subtitle-good2.vtt', 56 fixture: 'subtitle-good2.vtt',
66 mimeType: 'application/octet-stream' 57 mimeType: 'application/octet-stream'
67 }) 58 })
68 59
69 await createVideoCaption({ 60 await server.captions.add({
70 url: server.url,
71 accessToken: server.accessToken,
72 language: 'aa', 61 language: 'aa',
73 videoId, 62 videoId: id,
74 fixture: 'subtitle-good2.vtt', 63 fixture: 'subtitle-good2.vtt',
75 mimeType: 'application/octet-stream' 64 mimeType: 'application/octet-stream'
76 }) 65 })
77 } 66 }
78 67
79 const attributes4 = immutableAssign(attributes1, { name: attributes1.name + ' - 4', language: 'pl', nsfw: true }) 68 const attributes4 = { ...attributes1, name: attributes1.name + ' - 4', language: 'pl', nsfw: true }
80 await uploadVideo(server.url, server.accessToken, attributes4) 69 await server.videos.upload({ attributes: attributes4 })
81 70
82 await wait(1000) 71 await wait(1000)
83 72
84 startDate = new Date().toISOString() 73 startDate = new Date().toISOString()
85 74
86 const attributes5 = immutableAssign(attributes1, { name: attributes1.name + ' - 5', licence: 2, language: undefined }) 75 const attributes5 = { ...attributes1, name: attributes1.name + ' - 5', licence: 2, language: undefined }
87 await uploadVideo(server.url, server.accessToken, attributes5) 76 await server.videos.upload({ attributes: attributes5 })
88 77
89 const attributes6 = immutableAssign(attributes1, { name: attributes1.name + ' - 6', tags: [ 't1', 't2' ] }) 78 const attributes6 = { ...attributes1, name: attributes1.name + ' - 6', tags: [ 't1', 't2' ] }
90 await uploadVideo(server.url, server.accessToken, attributes6) 79 await server.videos.upload({ attributes: attributes6 })
91 80
92 const attributes7 = immutableAssign(attributes1, { 81 const attributes7 = { ...attributes1, name: attributes1.name + ' - 7', originallyPublishedAt: '2019-02-12T09:58:08.286Z' }
93 name: attributes1.name + ' - 7', 82 await server.videos.upload({ attributes: attributes7 })
94 originallyPublishedAt: '2019-02-12T09:58:08.286Z'
95 })
96 await uploadVideo(server.url, server.accessToken, attributes7)
97 83
98 const attributes8 = immutableAssign(attributes1, { name: attributes1.name + ' - 8', licence: 4 }) 84 const attributes8 = { ...attributes1, name: attributes1.name + ' - 8', licence: 4 }
99 await uploadVideo(server.url, server.accessToken, attributes8) 85 await server.videos.upload({ attributes: attributes8 })
100 } 86 }
101 87
102 { 88 {
@@ -107,9 +93,9 @@ describe('Test videos search', function () {
107 licence: 2, 93 licence: 2,
108 language: 'en' 94 language: 'en'
109 } 95 }
110 await uploadVideo(server.url, server.accessToken, attributes) 96 await server.videos.upload({ attributes: attributes })
111 97
112 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes, { name: attributes.name + ' duplicate' })) 98 await server.videos.upload({ attributes: { ...attributes, name: attributes.name + ' duplicate' } })
113 } 99 }
114 100
115 { 101 {
@@ -120,7 +106,7 @@ describe('Test videos search', function () {
120 licence: 3, 106 licence: 3,
121 language: 'pl' 107 language: 'pl'
122 } 108 }
123 await uploadVideo(server.url, server.accessToken, attributes) 109 await server.videos.upload({ attributes: attributes })
124 } 110 }
125 111
126 { 112 {
@@ -129,11 +115,11 @@ describe('Test videos search', function () {
129 tags: [ 'aaaa', 'bbbb', 'cccc' ], 115 tags: [ 'aaaa', 'bbbb', 'cccc' ],
130 category: 1 116 category: 1
131 } 117 }
132 await uploadVideo(server.url, server.accessToken, attributes1) 118 await server.videos.upload({ attributes: attributes1 })
133 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { category: 2 })) 119 await server.videos.upload({ attributes: { ...attributes1, category: 2 } })
134 120
135 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { tags: [ 'cccc', 'dddd' ] })) 121 await server.videos.upload({ attributes: { ...attributes1, tags: [ 'cccc', 'dddd' ] } })
136 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { tags: [ 'eeee', 'ffff' ] })) 122 await server.videos.upload({ attributes: { ...attributes1, tags: [ 'eeee', 'ffff' ] } })
137 } 123 }
138 124
139 { 125 {
@@ -141,24 +127,26 @@ describe('Test videos search', function () {
141 name: 'aaaa 2', 127 name: 'aaaa 2',
142 category: 1 128 category: 1
143 } 129 }
144 await uploadVideo(server.url, server.accessToken, attributes1) 130 await server.videos.upload({ attributes: attributes1 })
145 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { category: 2 })) 131 await server.videos.upload({ attributes: { ...attributes1, category: 2 } })
146 } 132 }
133
134 command = server.search
147 }) 135 })
148 136
149 it('Should make a simple search and not have results', async function () { 137 it('Should make a simple search and not have results', async function () {
150 const res = await searchVideo(server.url, 'abc') 138 const body = await command.searchVideos({ search: 'abc' })
151 139
152 expect(res.body.total).to.equal(0) 140 expect(body.total).to.equal(0)
153 expect(res.body.data).to.have.lengthOf(0) 141 expect(body.data).to.have.lengthOf(0)
154 }) 142 })
155 143
156 it('Should make a simple search and have results', async function () { 144 it('Should make a simple search and have results', async function () {
157 const res = await searchVideo(server.url, '4444 5555 duplicate') 145 const body = await command.searchVideos({ search: '4444 5555 duplicate' })
158 146
159 expect(res.body.total).to.equal(2) 147 expect(body.total).to.equal(2)
160 148
161 const videos = res.body.data 149 const videos = body.data
162 expect(videos).to.have.lengthOf(2) 150 expect(videos).to.have.lengthOf(2)
163 151
164 // bestmatch 152 // bestmatch
@@ -167,15 +155,15 @@ describe('Test videos search', function () {
167 }) 155 })
168 156
169 it('Should make a search on tags too, and have results', async function () { 157 it('Should make a search on tags too, and have results', async function () {
170 const query = { 158 const search = {
171 search: 'aaaa', 159 search: 'aaaa',
172 categoryOneOf: [ 1 ] 160 categoryOneOf: [ 1 ]
173 } 161 }
174 const res = await advancedVideosSearch(server.url, query) 162 const body = await command.advancedVideoSearch({ search })
175 163
176 expect(res.body.total).to.equal(2) 164 expect(body.total).to.equal(2)
177 165
178 const videos = res.body.data 166 const videos = body.data
179 expect(videos).to.have.lengthOf(2) 167 expect(videos).to.have.lengthOf(2)
180 168
181 // bestmatch 169 // bestmatch
@@ -184,14 +172,14 @@ describe('Test videos search', function () {
184 }) 172 })
185 173
186 it('Should filter on tags without a search', async function () { 174 it('Should filter on tags without a search', async function () {
187 const query = { 175 const search = {
188 tagsAllOf: [ 'bbbb' ] 176 tagsAllOf: [ 'bbbb' ]
189 } 177 }
190 const res = await advancedVideosSearch(server.url, query) 178 const body = await command.advancedVideoSearch({ search })
191 179
192 expect(res.body.total).to.equal(2) 180 expect(body.total).to.equal(2)
193 181
194 const videos = res.body.data 182 const videos = body.data
195 expect(videos).to.have.lengthOf(2) 183 expect(videos).to.have.lengthOf(2)
196 184
197 expect(videos[0].name).to.equal('9999') 185 expect(videos[0].name).to.equal('9999')
@@ -199,14 +187,14 @@ describe('Test videos search', function () {
199 }) 187 })
200 188
201 it('Should filter on category without a search', async function () { 189 it('Should filter on category without a search', async function () {
202 const query = { 190 const search = {
203 categoryOneOf: [ 3 ] 191 categoryOneOf: [ 3 ]
204 } 192 }
205 const res = await advancedVideosSearch(server.url, query) 193 const body = await command.advancedVideoSearch({ search: search })
206 194
207 expect(res.body.total).to.equal(1) 195 expect(body.total).to.equal(1)
208 196
209 const videos = res.body.data 197 const videos = body.data
210 expect(videos).to.have.lengthOf(1) 198 expect(videos).to.have.lengthOf(1)
211 199
212 expect(videos[0].name).to.equal('6666 7777 8888') 200 expect(videos[0].name).to.equal('6666 7777 8888')
@@ -218,11 +206,16 @@ describe('Test videos search', function () {
218 categoryOneOf: [ 1 ], 206 categoryOneOf: [ 1 ],
219 tagsOneOf: [ 'aAaa', 'ffff' ] 207 tagsOneOf: [ 'aAaa', 'ffff' ]
220 } 208 }
221 const res1 = await advancedVideosSearch(server.url, query)
222 expect(res1.body.total).to.equal(2)
223 209
224 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsOneOf: [ 'blabla' ] })) 210 {
225 expect(res2.body.total).to.equal(0) 211 const body = await command.advancedVideoSearch({ search: query })
212 expect(body.total).to.equal(2)
213 }
214
215 {
216 const body = await command.advancedVideoSearch({ search: { ...query, tagsOneOf: [ 'blabla' ] } })
217 expect(body.total).to.equal(0)
218 }
226 }) 219 })
227 220
228 it('Should search by tags (all of)', async function () { 221 it('Should search by tags (all of)', async function () {
@@ -231,14 +224,21 @@ describe('Test videos search', function () {
231 categoryOneOf: [ 1 ], 224 categoryOneOf: [ 1 ],
232 tagsAllOf: [ 'CCcc' ] 225 tagsAllOf: [ 'CCcc' ]
233 } 226 }
234 const res1 = await advancedVideosSearch(server.url, query)
235 expect(res1.body.total).to.equal(2)
236 227
237 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'blAbla' ] })) 228 {
238 expect(res2.body.total).to.equal(0) 229 const body = await command.advancedVideoSearch({ search: query })
230 expect(body.total).to.equal(2)
231 }
239 232
240 const res3 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'bbbb', 'CCCC' ] })) 233 {
241 expect(res3.body.total).to.equal(1) 234 const body = await command.advancedVideoSearch({ search: { ...query, tagsAllOf: [ 'blAbla' ] } })
235 expect(body.total).to.equal(0)
236 }
237
238 {
239 const body = await command.advancedVideoSearch({ search: { ...query, tagsAllOf: [ 'bbbb', 'CCCC' ] } })
240 expect(body.total).to.equal(1)
241 }
242 }) 242 })
243 243
244 it('Should search by category', async function () { 244 it('Should search by category', async function () {
@@ -246,12 +246,17 @@ describe('Test videos search', function () {
246 search: '6666', 246 search: '6666',
247 categoryOneOf: [ 3 ] 247 categoryOneOf: [ 3 ]
248 } 248 }
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 249
253 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { categoryOneOf: [ 2 ] })) 250 {
254 expect(res2.body.total).to.equal(0) 251 const body = await command.advancedVideoSearch({ search: query })
252 expect(body.total).to.equal(1)
253 expect(body.data[0].name).to.equal('6666 7777 8888')
254 }
255
256 {
257 const body = await command.advancedVideoSearch({ search: { ...query, categoryOneOf: [ 2 ] } })
258 expect(body.total).to.equal(0)
259 }
255 }) 260 })
256 261
257 it('Should search by licence', async function () { 262 it('Should search by licence', async function () {
@@ -259,13 +264,18 @@ describe('Test videos search', function () {
259 search: '4444 5555', 264 search: '4444 5555',
260 licenceOneOf: [ 2 ] 265 licenceOneOf: [ 2 ]
261 } 266 }
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 267
267 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { licenceOneOf: [ 3 ] })) 268 {
268 expect(res2.body.total).to.equal(0) 269 const body = await command.advancedVideoSearch({ search: query })
270 expect(body.total).to.equal(2)
271 expect(body.data[0].name).to.equal('3333 4444 5555')
272 expect(body.data[1].name).to.equal('3333 4444 5555 duplicate')
273 }
274
275 {
276 const body = await command.advancedVideoSearch({ search: { ...query, licenceOneOf: [ 3 ] } })
277 expect(body.total).to.equal(0)
278 }
269 }) 279 })
270 280
271 it('Should search by languages', async function () { 281 it('Should search by languages', async function () {
@@ -275,23 +285,23 @@ describe('Test videos search', function () {
275 } 285 }
276 286
277 { 287 {
278 const res = await advancedVideosSearch(server.url, query) 288 const body = await command.advancedVideoSearch({ search: query })
279 expect(res.body.total).to.equal(2) 289 expect(body.total).to.equal(2)
280 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 290 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
281 expect(res.body.data[1].name).to.equal('1111 2222 3333 - 4') 291 expect(body.data[1].name).to.equal('1111 2222 3333 - 4')
282 } 292 }
283 293
284 { 294 {
285 const res = await advancedVideosSearch(server.url, immutableAssign(query, { languageOneOf: [ 'pl', 'en', '_unknown' ] })) 295 const body = await command.advancedVideoSearch({ search: { ...query, languageOneOf: [ 'pl', 'en', '_unknown' ] } })
286 expect(res.body.total).to.equal(3) 296 expect(body.total).to.equal(3)
287 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 297 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
288 expect(res.body.data[1].name).to.equal('1111 2222 3333 - 4') 298 expect(body.data[1].name).to.equal('1111 2222 3333 - 4')
289 expect(res.body.data[2].name).to.equal('1111 2222 3333 - 5') 299 expect(body.data[2].name).to.equal('1111 2222 3333 - 5')
290 } 300 }
291 301
292 { 302 {
293 const res = await advancedVideosSearch(server.url, immutableAssign(query, { languageOneOf: [ 'eo' ] })) 303 const body = await command.advancedVideoSearch({ search: { ...query, languageOneOf: [ 'eo' ] } })
294 expect(res.body.total).to.equal(0) 304 expect(body.total).to.equal(0)
295 } 305 }
296 }) 306 })
297 307
@@ -301,10 +311,10 @@ describe('Test videos search', function () {
301 startDate 311 startDate
302 } 312 }
303 313
304 const res = await advancedVideosSearch(server.url, query) 314 const body = await command.advancedVideoSearch({ search: query })
305 expect(res.body.total).to.equal(4) 315 expect(body.total).to.equal(4)
306 316
307 const videos = res.body.data 317 const videos = body.data
308 expect(videos[0].name).to.equal('1111 2222 3333 - 5') 318 expect(videos[0].name).to.equal('1111 2222 3333 - 5')
309 expect(videos[1].name).to.equal('1111 2222 3333 - 6') 319 expect(videos[1].name).to.equal('1111 2222 3333 - 6')
310 expect(videos[2].name).to.equal('1111 2222 3333 - 7') 320 expect(videos[2].name).to.equal('1111 2222 3333 - 7')
@@ -320,10 +330,10 @@ describe('Test videos search', function () {
320 licenceOneOf: [ 1, 4 ] 330 licenceOneOf: [ 1, 4 ]
321 } 331 }
322 332
323 const res = await advancedVideosSearch(server.url, query) 333 const body = await command.advancedVideoSearch({ search: query })
324 expect(res.body.total).to.equal(4) 334 expect(body.total).to.equal(4)
325 335
326 const videos = res.body.data 336 const videos = body.data
327 expect(videos[0].name).to.equal('1111 2222 3333') 337 expect(videos[0].name).to.equal('1111 2222 3333')
328 expect(videos[1].name).to.equal('1111 2222 3333 - 6') 338 expect(videos[1].name).to.equal('1111 2222 3333 - 6')
329 expect(videos[2].name).to.equal('1111 2222 3333 - 7') 339 expect(videos[2].name).to.equal('1111 2222 3333 - 7')
@@ -340,10 +350,10 @@ describe('Test videos search', function () {
340 sort: '-name' 350 sort: '-name'
341 } 351 }
342 352
343 const res = await advancedVideosSearch(server.url, query) 353 const body = await command.advancedVideoSearch({ search: query })
344 expect(res.body.total).to.equal(4) 354 expect(body.total).to.equal(4)
345 355
346 const videos = res.body.data 356 const videos = body.data
347 expect(videos[0].name).to.equal('1111 2222 3333 - 8') 357 expect(videos[0].name).to.equal('1111 2222 3333 - 8')
348 expect(videos[1].name).to.equal('1111 2222 3333 - 7') 358 expect(videos[1].name).to.equal('1111 2222 3333 - 7')
349 expect(videos[2].name).to.equal('1111 2222 3333 - 6') 359 expect(videos[2].name).to.equal('1111 2222 3333 - 6')
@@ -362,10 +372,10 @@ describe('Test videos search', function () {
362 count: 1 372 count: 1
363 } 373 }
364 374
365 const res = await advancedVideosSearch(server.url, query) 375 const body = await command.advancedVideoSearch({ search: query })
366 expect(res.body.total).to.equal(4) 376 expect(body.total).to.equal(4)
367 377
368 const videos = res.body.data 378 const videos = body.data
369 expect(videos[0].name).to.equal('1111 2222 3333 - 8') 379 expect(videos[0].name).to.equal('1111 2222 3333 - 8')
370 }) 380 })
371 381
@@ -381,10 +391,10 @@ describe('Test videos search', function () {
381 count: 1 391 count: 1
382 } 392 }
383 393
384 const res = await advancedVideosSearch(server.url, query) 394 const body = await command.advancedVideoSearch({ search: query })
385 expect(res.body.total).to.equal(4) 395 expect(body.total).to.equal(4)
386 396
387 const videos = res.body.data 397 const videos = body.data
388 expect(videos[0].name).to.equal('1111 2222 3333') 398 expect(videos[0].name).to.equal('1111 2222 3333')
389 }) 399 })
390 400
@@ -398,99 +408,102 @@ describe('Test videos search', function () {
398 } 408 }
399 409
400 { 410 {
401 const query = immutableAssign(baseQuery, { originallyPublishedStartDate: '2019-02-11T09:58:08.286Z' }) 411 const query = { ...baseQuery, originallyPublishedStartDate: '2019-02-11T09:58:08.286Z' }
402 const res = await advancedVideosSearch(server.url, query) 412 const body = await command.advancedVideoSearch({ search: query })
403 413
404 expect(res.body.total).to.equal(1) 414 expect(body.total).to.equal(1)
405 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7') 415 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
406 } 416 }
407 417
408 { 418 {
409 const query = immutableAssign(baseQuery, { originallyPublishedEndDate: '2019-03-11T09:58:08.286Z' }) 419 const query = { ...baseQuery, originallyPublishedEndDate: '2019-03-11T09:58:08.286Z' }
410 const res = await advancedVideosSearch(server.url, query) 420 const body = await command.advancedVideoSearch({ search: query })
411 421
412 expect(res.body.total).to.equal(1) 422 expect(body.total).to.equal(1)
413 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7') 423 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
414 } 424 }
415 425
416 { 426 {
417 const query = immutableAssign(baseQuery, { originallyPublishedEndDate: '2019-01-11T09:58:08.286Z' }) 427 const query = { ...baseQuery, originallyPublishedEndDate: '2019-01-11T09:58:08.286Z' }
418 const res = await advancedVideosSearch(server.url, query) 428 const body = await command.advancedVideoSearch({ search: query })
419 429
420 expect(res.body.total).to.equal(0) 430 expect(body.total).to.equal(0)
421 } 431 }
422 432
423 { 433 {
424 const query = immutableAssign(baseQuery, { originallyPublishedStartDate: '2019-03-11T09:58:08.286Z' }) 434 const query = { ...baseQuery, originallyPublishedStartDate: '2019-03-11T09:58:08.286Z' }
425 const res = await advancedVideosSearch(server.url, query) 435 const body = await command.advancedVideoSearch({ search: query })
426 436
427 expect(res.body.total).to.equal(0) 437 expect(body.total).to.equal(0)
428 } 438 }
429 439
430 { 440 {
431 const query = immutableAssign(baseQuery, { 441 const query = {
442 ...baseQuery,
432 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z', 443 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
433 originallyPublishedEndDate: '2019-01-10T09:58:08.286Z' 444 originallyPublishedEndDate: '2019-01-10T09:58:08.286Z'
434 }) 445 }
435 const res = await advancedVideosSearch(server.url, query) 446 const body = await command.advancedVideoSearch({ search: query })
436 447
437 expect(res.body.total).to.equal(0) 448 expect(body.total).to.equal(0)
438 } 449 }
439 450
440 { 451 {
441 const query = immutableAssign(baseQuery, { 452 const query = {
453 ...baseQuery,
442 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z', 454 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
443 originallyPublishedEndDate: '2019-04-11T09:58:08.286Z' 455 originallyPublishedEndDate: '2019-04-11T09:58:08.286Z'
444 }) 456 }
445 const res = await advancedVideosSearch(server.url, query) 457 const body = await command.advancedVideoSearch({ search: query })
446 458
447 expect(res.body.total).to.equal(1) 459 expect(body.total).to.equal(1)
448 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7') 460 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
449 } 461 }
450 }) 462 })
451 463
452 it('Should search by UUID', async function () { 464 it('Should search by UUID', async function () {
453 const search = videoUUID 465 const search = videoUUID
454 const res = await advancedVideosSearch(server.url, { search }) 466 const body = await command.advancedVideoSearch({ search: { search } })
455 467
456 expect(res.body.total).to.equal(1) 468 expect(body.total).to.equal(1)
457 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 469 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
458 }) 470 })
459 471
460 it('Should search by live', async function () { 472 it('Should search by live', async function () {
461 this.timeout(30000) 473 this.timeout(30000)
462 474
463 { 475 {
464 const options = { 476 const newConfig = {
465 search: { 477 search: {
466 searchIndex: { enabled: false } 478 searchIndex: { enabled: false }
467 }, 479 },
468 live: { enabled: true } 480 live: { enabled: true }
469 } 481 }
470 await updateCustomSubConfig(server.url, server.accessToken, options) 482 await server.config.updateCustomSubConfig({ newConfig })
471 } 483 }
472 484
473 { 485 {
474 const res = await advancedVideosSearch(server.url, { isLive: true }) 486 const body = await command.advancedVideoSearch({ search: { isLive: true } })
475 487
476 expect(res.body.total).to.equal(0) 488 expect(body.total).to.equal(0)
477 expect(res.body.data).to.have.lengthOf(0) 489 expect(body.data).to.have.lengthOf(0)
478 } 490 }
479 491
480 { 492 {
481 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.videoChannel.id } 493 const liveCommand = server.live
482 const resLive = await createLive(server.url, server.accessToken, liveOptions) 494
483 const liveVideoId = resLive.body.video.uuid 495 const liveAttributes = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.store.channel.id }
496 const live = await liveCommand.create({ fields: liveAttributes })
484 497
485 const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId) 498 const ffmpegCommand = await liveCommand.sendRTMPStreamInVideo({ videoId: live.id })
486 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 499 await liveCommand.waitUntilPublished({ videoId: live.id })
487 500
488 const res = await advancedVideosSearch(server.url, { isLive: true }) 501 const body = await command.advancedVideoSearch({ search: { isLive: true } })
489 502
490 expect(res.body.total).to.equal(1) 503 expect(body.total).to.equal(1)
491 expect(res.body.data[0].name).to.equal('live') 504 expect(body.data[0].name).to.equal('live')
492 505
493 await stopFfmpeg(command) 506 await stopFfmpeg(ffmpegCommand)
494 } 507 }
495 }) 508 })
496 509
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..ae86b380f 100644
--- a/server/tests/api/server/email.ts
+++ b/server/tests/api/server/email.ts
@@ -2,32 +2,13 @@
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
@@ -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 { uuid, id } = await server.videos.upload({ attributes })
84 videoUUID = res.body.video.uuid 63 videoUUID = uuid
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 })
@@ -190,7 +184,7 @@ describe('Test emails', function () {
190 this.timeout(10000) 184 this.timeout(10000)
191 185
192 const reason = 'my super bad reason' 186 const reason = 'my super bad reason'
193 await reportAbuse({ url: server.url, token: server.accessToken, videoId, reason }) 187 await server.abuses.report({ videoId, reason })
194 188
195 await waitJobs(server) 189 await waitJobs(server)
196 expect(emails).to.have.lengthOf(3) 190 expect(emails).to.have.lengthOf(3)
@@ -211,7 +205,7 @@ describe('Test emails', function () {
211 this.timeout(10000) 205 this.timeout(10000)
212 206
213 const reason = 'my super bad reason' 207 const reason = 'my super bad reason'
214 await blockUser(server.url, userId, server.accessToken, HttpStatusCode.NO_CONTENT_204, reason) 208 await server.users.banUser({ userId, reason })
215 209
216 await waitJobs(server) 210 await waitJobs(server)
217 expect(emails).to.have.lengthOf(4) 211 expect(emails).to.have.lengthOf(4)
@@ -229,7 +223,7 @@ describe('Test emails', function () {
229 it('Should send the notification email when unblocking a user', async function () { 223 it('Should send the notification email when unblocking a user', async function () {
230 this.timeout(10000) 224 this.timeout(10000)
231 225
232 await unblockUser(server.url, userId, server.accessToken, HttpStatusCode.NO_CONTENT_204) 226 await server.users.unbanUser({ userId })
233 227
234 await waitJobs(server) 228 await waitJobs(server)
235 expect(emails).to.have.lengthOf(5) 229 expect(emails).to.have.lengthOf(5)
@@ -249,7 +243,7 @@ describe('Test emails', function () {
249 this.timeout(10000) 243 this.timeout(10000)
250 244
251 const reason = 'my super reason' 245 const reason = 'my super reason'
252 await addVideoToBlacklist(server.url, server.accessToken, videoUserUUID, reason) 246 await server.blacklist.add({ videoId: videoUserUUID, reason })
253 247
254 await waitJobs(server) 248 await waitJobs(server)
255 expect(emails).to.have.lengthOf(6) 249 expect(emails).to.have.lengthOf(6)
@@ -267,7 +261,7 @@ describe('Test emails', function () {
267 it('Should send the notification email', async function () { 261 it('Should send the notification email', async function () {
268 this.timeout(10000) 262 this.timeout(10000)
269 263
270 await removeVideoFromBlacklist(server.url, server.accessToken, videoUserUUID) 264 await server.blacklist.remove({ videoId: videoUserUUID })
271 265
272 await waitJobs(server) 266 await waitJobs(server)
273 expect(emails).to.have.lengthOf(7) 267 expect(emails).to.have.lengthOf(7)
@@ -292,7 +286,7 @@ describe('Test emails', function () {
292 it('Should ask to send the verification email', async function () { 286 it('Should ask to send the verification email', async function () {
293 this.timeout(10000) 287 this.timeout(10000)
294 288
295 await askSendVerifyEmail(server.url, 'user_1@example.com') 289 await server.users.askSendVerifyEmail({ email: 'user_1@example.com' })
296 290
297 await waitJobs(server) 291 await waitJobs(server)
298 expect(emails).to.have.lengthOf(8) 292 expect(emails).to.have.lengthOf(8)
@@ -318,11 +312,16 @@ describe('Test emails', function () {
318 }) 312 })
319 313
320 it('Should not verify the email with an invalid verification string', async function () { 314 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) 315 await server.users.verifyEmail({
316 userId,
317 verificationString: verificationString + 'b',
318 isPendingEmail: false,
319 expectedStatus: HttpStatusCode.FORBIDDEN_403
320 })
322 }) 321 })
323 322
324 it('Should verify the email', async function () { 323 it('Should verify the email', async function () {
325 await verifyEmail(server.url, userId, verificationString) 324 await server.users.verifyEmail({ userId, verificationString })
326 }) 325 })
327 }) 326 })
328 327
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..efa890ad4 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, {}, [ '--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..225145957 100644
--- a/server/tests/api/users/users-multiple-servers.ts
+++ b/server/tests/api/users/users-multiple-servers.ts
@@ -1,32 +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 { 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 setAccessTokensToServers,
14 getVideoChannelsList, 14 testImage,
15 removeUser, 15 waitJobs
16 updateMyUser, 16} from '@shared/extra-utils'
17 userLogin 17import { User } from '@shared/models'
18} from '../../../../shared/extra-utils'
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 18
26const expect = chai.expect 19const expect = chai.expect
27 20
28describe('Test users with multiple servers', function () { 21describe('Test users with multiple servers', function () {
29 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
30 let user: User 23 let user: User
31 let userId: number 24 let userId: number
32 let videoUUID: string 25 let videoUUID: string
@@ -36,7 +29,7 @@ describe('Test users with multiple servers', function () {
36 before(async function () { 29 before(async function () {
37 this.timeout(120_000) 30 this.timeout(120_000)
38 31
39 servers = await flushAndRunMultipleServers(3) 32 servers = await createMultipleServers(3)
40 33
41 // Get the access tokens 34 // Get the access tokens
42 await setAccessTokensToServers(servers) 35 await setAccessTokensToServers(servers)
@@ -49,26 +42,21 @@ describe('Test users with multiple servers', function () {
49 await doubleFollow(servers[1], servers[2]) 42 await doubleFollow(servers[1], servers[2])
50 43
51 // The root user of server 1 is propagated to servers 2 and 3 44 // The root user of server 1 is propagated to servers 2 and 3
52 await uploadVideo(servers[0].url, servers[0].accessToken, {}) 45 await servers[0].videos.upload()
53 46
54 { 47 {
55 const user = { 48 const user = {
56 username: 'user1', 49 username: 'user1',
57 password: 'password' 50 password: 'password'
58 } 51 }
59 const res = await createUser({ 52 const created = await servers[0].users.create(user)
60 url: servers[0].url, 53 userId = created.id
61 accessToken: servers[0].accessToken, 54 userAccessToken = await servers[0].login.getAccessToken(user)
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 } 60 }
73 61
74 await waitJobs(servers) 62 await waitJobs(servers)
@@ -77,15 +65,9 @@ describe('Test users with multiple servers', function () {
77 it('Should be able to update my display name', async function () { 65 it('Should be able to update my display name', async function () {
78 this.timeout(10000) 66 this.timeout(10000)
79 67
80 await updateMyUser({ 68 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 69
70 user = await servers[0].users.getMyInfo()
89 expect(user.account.displayName).to.equal('my super display name') 71 expect(user.account.displayName).to.equal('my super display name')
90 72
91 await waitJobs(servers) 73 await waitJobs(servers)
@@ -94,14 +76,9 @@ describe('Test users with multiple servers', function () {
94 it('Should be able to update my description', async function () { 76 it('Should be able to update my description', async function () {
95 this.timeout(10_000) 77 this.timeout(10_000)
96 78
97 await updateMyUser({ 79 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 80
103 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 81 user = await servers[0].users.getMyInfo()
104 user = res.body
105 expect(user.account.displayName).to.equal('my super display name') 82 expect(user.account.displayName).to.equal('my super display name')
106 expect(user.account.description).to.equal('my super description updated') 83 expect(user.account.description).to.equal('my super description updated')
107 84
@@ -113,15 +90,9 @@ describe('Test users with multiple servers', function () {
113 90
114 const fixture = 'avatar2.png' 91 const fixture = 'avatar2.png'
115 92
116 await updateMyAvatar({ 93 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 94
95 user = await servers[0].users.getMyInfo()
125 userAvatarFilename = user.account.avatar.path 96 userAvatarFilename = user.account.avatar.path
126 97
127 await testImage(servers[0].url, 'avatar2-resized', userAvatarFilename, '.png') 98 await testImage(servers[0].url, 'avatar2-resized', userAvatarFilename, '.png')
@@ -133,13 +104,12 @@ describe('Test users with multiple servers', function () {
133 let createdAt: string | Date 104 let createdAt: string | Date
134 105
135 for (const server of servers) { 106 for (const server of servers) {
136 const resAccounts = await getAccountsList(server.url, '-createdAt') 107 const body = await server.accounts.list({ sort: '-createdAt' })
137 108
138 const resList = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port) as Account 109 const resList = body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port)
139 expect(resList).not.to.be.undefined 110 expect(resList).not.to.be.undefined
140 111
141 const resAccount = await getAccount(server.url, resList.name + '@' + resList.host) 112 const account = await server.accounts.get({ accountName: resList.name + '@' + resList.host })
142 const account = resAccount.body as Account
143 113
144 if (!createdAt) createdAt = account.createdAt 114 if (!createdAt) createdAt = account.createdAt
145 115
@@ -161,31 +131,29 @@ describe('Test users with multiple servers', function () {
161 131
162 it('Should list account videos', async function () { 132 it('Should list account videos', async function () {
163 for (const server of servers) { 133 for (const server of servers) {
164 const res = await getAccountVideos(server.url, server.accessToken, 'user1@localhost:' + servers[0].port, 0, 5) 134 const { total, data } = await server.videos.listByAccount({ handle: 'user1@localhost:' + servers[0].port })
165 135
166 expect(res.body.total).to.equal(1) 136 expect(total).to.equal(1)
167 expect(res.body.data).to.be.an('array') 137 expect(data).to.be.an('array')
168 expect(res.body.data).to.have.lengthOf(1) 138 expect(data).to.have.lengthOf(1)
169 expect(res.body.data[0].uuid).to.equal(videoUUID) 139 expect(data[0].uuid).to.equal(videoUUID)
170 } 140 }
171 }) 141 })
172 142
173 it('Should search through account videos', async function () { 143 it('Should search through account videos', async function () {
174 this.timeout(10_000) 144 this.timeout(10_000)
175 145
176 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'Kami no chikara' }) 146 const created = await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'Kami no chikara' } })
177 147
178 await waitJobs(servers) 148 await waitJobs(servers)
179 149
180 for (const server of servers) { 150 for (const server of servers) {
181 const res = await getAccountVideos(server.url, server.accessToken, 'user1@localhost:' + servers[0].port, 0, 5, undefined, { 151 const { total, data } = await server.videos.listByAccount({ handle: 'user1@localhost:' + servers[0].port, search: 'Kami' })
182 search: 'Kami' 152
183 }) 153 expect(total).to.equal(1)
184 154 expect(data).to.be.an('array')
185 expect(res.body.total).to.equal(1) 155 expect(data).to.have.lengthOf(1)
186 expect(res.body.data).to.be.an('array') 156 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 } 157 }
190 }) 158 })
191 159
@@ -193,32 +161,28 @@ describe('Test users with multiple servers', function () {
193 this.timeout(10_000) 161 this.timeout(10_000)
194 162
195 for (const server of servers) { 163 for (const server of servers) {
196 const resAccounts = await getAccountsList(server.url, '-createdAt') 164 const body = await server.accounts.list({ sort: '-createdAt' })
197 165
198 const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account 166 const accountDeleted = body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port)
199 expect(accountDeleted).not.to.be.undefined 167 expect(accountDeleted).not.to.be.undefined
200 168
201 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) 169 const { data } = await server.channels.list()
202 const videoChannelDeleted = resVideoChannels.body.data.find(a => { 170 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 171 expect(videoChannelDeleted).not.to.be.undefined
206 } 172 }
207 173
208 await removeUser(servers[0].url, userId, servers[0].accessToken) 174 await servers[0].users.remove({ userId })
209 175
210 await waitJobs(servers) 176 await waitJobs(servers)
211 177
212 for (const server of servers) { 178 for (const server of servers) {
213 const resAccounts = await getAccountsList(server.url, '-createdAt') 179 const body = await server.accounts.list({ sort: '-createdAt' })
214 180
215 const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account 181 const accountDeleted = body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port)
216 expect(accountDeleted).to.be.undefined 182 expect(accountDeleted).to.be.undefined
217 183
218 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) 184 const { data } = await server.channels.list()
219 const videoChannelDeleted = resVideoChannels.body.data.find(a => { 185 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 186 expect(videoChannelDeleted).to.be.undefined
223 } 187 }
224 }) 188 })
@@ -231,7 +195,7 @@ describe('Test users with multiple servers', function () {
231 195
232 it('Should not have video files', async () => { 196 it('Should not have video files', async () => {
233 for (const server of servers) { 197 for (const server of servers) {
234 await checkVideoFilesWereRemoved(videoUUID, server.internalServerNumber) 198 await checkVideoFilesWereRemoved(videoUUID, server)
235 } 199 }
236 }) 200 })
237 201
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..15c3ae6d6 100644
--- a/server/tests/api/videos/audio-only.ts
+++ b/server/tests/api/videos/audio-only.ts
@@ -4,23 +4,12 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path' 5import { join } from 'path'
6import { getAudioStream, getVideoStreamSize } from '@server/helpers/ffprobe-utils' 6import { getAudioStream, getVideoStreamSize } from '@server/helpers/ffprobe-utils'
7import { 7import { 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 8
20const expect = chai.expect 9const expect = chai.expect
21 10
22describe('Test audio only video transcoding', function () { 11describe('Test audio only video transcoding', function () {
23 let servers: ServerInfo[] = [] 12 let servers: PeerTubeServer[] = []
24 let videoUUID: string 13 let videoUUID: string
25 14
26 before(async function () { 15 before(async function () {
@@ -47,7 +36,7 @@ describe('Test audio only video transcoding', function () {
47 } 36 }
48 } 37 }
49 } 38 }
50 servers = await flushAndRunMultipleServers(2, configOverride) 39 servers = await createMultipleServers(2, configOverride)
51 40
52 // Get the access tokens 41 // Get the access tokens
53 await setAccessTokensToServers(servers) 42 await setAccessTokensToServers(servers)
@@ -59,15 +48,13 @@ describe('Test audio only video transcoding', function () {
59 it('Should upload a video and transcode it', async function () { 48 it('Should upload a video and transcode it', async function () {
60 this.timeout(120000) 49 this.timeout(120000)
61 50
62 const resUpload = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'audio only' }) 51 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'audio only' } })
63 videoUUID = resUpload.body.video.uuid 52 videoUUID = uuid
64 53
65 await waitJobs(servers) 54 await waitJobs(servers)
66 55
67 for (const server of servers) { 56 for (const server of servers) {
68 const res = await getVideo(server.url, videoUUID) 57 const video = await server.videos.get({ id: videoUUID })
69 const video: VideoDetails = res.body
70
71 expect(video.streamingPlaylists).to.have.lengthOf(1) 58 expect(video.streamingPlaylists).to.have.lengthOf(1)
72 59
73 for (const files of [ video.files, video.streamingPlaylists[0].files ]) { 60 for (const files of [ video.files, video.streamingPlaylists[0].files ]) {
@@ -81,8 +68,8 @@ describe('Test audio only video transcoding', function () {
81 68
82 it('0p transcoded video should not have video', async function () { 69 it('0p transcoded video should not have video', async function () {
83 const paths = [ 70 const paths = [
84 buildServerDirectory(servers[0], join('videos', videoUUID + '-0.mp4')), 71 servers[0].servers.buildDirectory(join('videos', videoUUID + '-0.mp4')),
85 buildServerDirectory(servers[0], join('streaming-playlists', 'hls', videoUUID, videoUUID + '-0-fragmented.mp4')) 72 servers[0].servers.buildDirectory(join('streaming-playlists', 'hls', videoUUID, videoUUID + '-0-fragmented.mp4'))
86 ] 73 ]
87 74
88 for (const path of paths) { 75 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..d916abb09 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -3,49 +3,28 @@
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,
19 getVideo,
20 getVideoChannelsList,
21 getVideosList,
22 rateVideo,
23 removeVideo,
24 ServerInfo,
25 setAccessTokensToServers, 16 setAccessTokensToServers,
26 testImage, 17 testImage,
27 updateVideo,
28 uploadVideo,
29 userLogin,
30 viewVideo,
31 wait, 18 wait,
19 waitJobs,
32 webtorrentAdd 20 webtorrentAdd
33} from '../../../../shared/extra-utils' 21} from '@shared/extra-utils'
34import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 22import { 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 23
45const expect = chai.expect 24const expect = chai.expect
46 25
47describe('Test multiple servers', function () { 26describe('Test multiple servers', function () {
48 let servers: ServerInfo[] = [] 27 let servers: PeerTubeServer[] = []
49 const toRemove = [] 28 const toRemove = []
50 let videoUUID = '' 29 let videoUUID = ''
51 let videoChannelId: number 30 let videoChannelId: number
@@ -53,7 +32,7 @@ describe('Test multiple servers', function () {
53 before(async function () { 32 before(async function () {
54 this.timeout(120000) 33 this.timeout(120000)
55 34
56 servers = await flushAndRunMultipleServers(3) 35 servers = await createMultipleServers(3)
57 36
58 // Get the access tokens 37 // Get the access tokens
59 await setAccessTokensToServers(servers) 38 await setAccessTokensToServers(servers)
@@ -64,9 +43,9 @@ describe('Test multiple servers', function () {
64 displayName: 'my channel', 43 displayName: 'my channel',
65 description: 'super channel' 44 description: 'super channel'
66 } 45 }
67 await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) 46 await servers[0].channels.create({ attributes: videoChannel })
68 const channelRes = await getVideoChannelsList(servers[0].url, 0, 1) 47 const { data } = await servers[0].channels.list({ start: 0, count: 1 })
69 videoChannelId = channelRes.body.data[0].id 48 videoChannelId = data[0].id
70 } 49 }
71 50
72 // Server 1 and server 2 follow each other 51 // Server 1 and server 2 follow each other
@@ -79,10 +58,9 @@ describe('Test multiple servers', function () {
79 58
80 it('Should not have videos for all servers', async function () { 59 it('Should not have videos for all servers', async function () {
81 for (const server of servers) { 60 for (const server of servers) {
82 const res = await getVideosList(server.url) 61 const { data } = await server.videos.list()
83 const videos = res.body.data 62 expect(data).to.be.an('array')
84 expect(videos).to.be.an('array') 63 expect(data.length).to.equal(0)
85 expect(videos.length).to.equal(0)
86 } 64 }
87 }) 65 })
88 66
@@ -90,7 +68,7 @@ describe('Test multiple servers', function () {
90 it('Should upload the video on server 1 and propagate on each server', async function () { 68 it('Should upload the video on server 1 and propagate on each server', async function () {
91 this.timeout(25000) 69 this.timeout(25000)
92 70
93 const videoAttributes = { 71 const attributes = {
94 name: 'my super name for server 1', 72 name: 'my super name for server 1',
95 category: 5, 73 category: 5,
96 licence: 4, 74 licence: 4,
@@ -103,7 +81,7 @@ describe('Test multiple servers', function () {
103 channelId: videoChannelId, 81 channelId: videoChannelId,
104 fixture: 'video_short1.webm' 82 fixture: 'video_short1.webm'
105 } 83 }
106 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 84 await servers[0].videos.upload({ attributes })
107 85
108 await waitJobs(servers) 86 await waitJobs(servers)
109 87
@@ -146,14 +124,13 @@ describe('Test multiple servers', function () {
146 ] 124 ]
147 } 125 }
148 126
149 const res = await getVideosList(server.url) 127 const { data } = await server.videos.list()
150 const videos = res.body.data 128 expect(data).to.be.an('array')
151 expect(videos).to.be.an('array') 129 expect(data.length).to.equal(1)
152 expect(videos.length).to.equal(1) 130 const video = data[0]
153 const video = videos[0]
154 131
155 await completeVideoCheck(server.url, video, checkAttributes) 132 await completeVideoCheck(server, video, checkAttributes)
156 publishedAt = video.publishedAt 133 publishedAt = video.publishedAt as string
157 } 134 }
158 }) 135 })
159 136
@@ -164,10 +141,10 @@ describe('Test multiple servers', function () {
164 username: 'user1', 141 username: 'user1',
165 password: 'super_password' 142 password: 'super_password'
166 } 143 }
167 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 144 await servers[1].users.create({ username: user.username, password: user.password })
168 const userAccessToken = await userLogin(servers[1], user) 145 const userAccessToken = await servers[1].login.getAccessToken(user)
169 146
170 const videoAttributes = { 147 const attributes = {
171 name: 'my super name for server 2', 148 name: 'my super name for server 2',
172 category: 4, 149 category: 4,
173 licence: 3, 150 licence: 3,
@@ -180,7 +157,7 @@ describe('Test multiple servers', function () {
180 thumbnailfile: 'thumbnail.jpg', 157 thumbnailfile: 'thumbnail.jpg',
181 previewfile: 'preview.jpg' 158 previewfile: 'preview.jpg'
182 } 159 }
183 await uploadVideo(servers[1].url, userAccessToken, videoAttributes, HttpStatusCode.OK_200, 'resumable') 160 await servers[1].videos.upload({ token: userAccessToken, attributes, mode: 'resumable' })
184 161
185 // Transcoding 162 // Transcoding
186 await waitJobs(servers) 163 await waitJobs(servers)
@@ -235,65 +212,67 @@ describe('Test multiple servers', function () {
235 previewfile: 'preview' 212 previewfile: 'preview'
236 } 213 }
237 214
238 const res = await getVideosList(server.url) 215 const { data } = await server.videos.list()
239 const videos = res.body.data 216 expect(data).to.be.an('array')
240 expect(videos).to.be.an('array') 217 expect(data.length).to.equal(2)
241 expect(videos.length).to.equal(2) 218 const video = data[1]
242 const video = videos[1]
243 219
244 await completeVideoCheck(server.url, video, checkAttributes) 220 await completeVideoCheck(server, video, checkAttributes)
245 } 221 }
246 }) 222 })
247 223
248 it('Should upload two videos on server 3 and propagate on each server', async function () { 224 it('Should upload two videos on server 3 and propagate on each server', async function () {
249 this.timeout(45000) 225 this.timeout(45000)
250 226
251 const videoAttributes1 = { 227 {
252 name: 'my super name for server 3', 228 const attributes = {
253 category: 6, 229 name: 'my super name for server 3',
254 licence: 5, 230 category: 6,
255 language: 'de', 231 licence: 5,
256 nsfw: true, 232 language: 'de',
257 description: 'my super description for server 3', 233 nsfw: true,
258 support: 'my super support text for server 3', 234 description: 'my super description for server 3',
259 tags: [ 'tag1p3' ], 235 support: 'my super support text for server 3',
260 fixture: 'video_short3.webm' 236 tags: [ 'tag1p3' ],
237 fixture: 'video_short3.webm'
238 }
239 await servers[2].videos.upload({ attributes })
261 } 240 }
262 await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1) 241
263 242 {
264 const videoAttributes2 = { 243 const attributes = {
265 name: 'my super name for server 3-2', 244 name: 'my super name for server 3-2',
266 category: 7, 245 category: 7,
267 licence: 6, 246 licence: 6,
268 language: 'ko', 247 language: 'ko',
269 nsfw: false, 248 nsfw: false,
270 description: 'my super description for server 3-2', 249 description: 'my super description for server 3-2',
271 support: 'my super support text for server 3-2', 250 support: 'my super support text for server 3-2',
272 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], 251 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
273 fixture: 'video_short.webm' 252 fixture: 'video_short.webm'
253 }
254 await servers[2].videos.upload({ attributes })
274 } 255 }
275 await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes2)
276 256
277 await waitJobs(servers) 257 await waitJobs(servers)
278 258
279 // All servers should have this video 259 // All servers should have this video
280 for (const server of servers) { 260 for (const server of servers) {
281 const isLocal = server.url === 'http://localhost:' + servers[2].port 261 const isLocal = server.url === 'http://localhost:' + servers[2].port
282 const res = await getVideosList(server.url) 262 const { data } = await server.videos.list()
283 263
284 const videos = res.body.data 264 expect(data).to.be.an('array')
285 expect(videos).to.be.an('array') 265 expect(data.length).to.equal(4)
286 expect(videos.length).to.equal(4)
287 266
288 // We not sure about the order of the two last uploads 267 // We not sure about the order of the two last uploads
289 let video1 = null 268 let video1 = null
290 let video2 = null 269 let video2 = null
291 if (videos[2].name === 'my super name for server 3') { 270 if (data[2].name === 'my super name for server 3') {
292 video1 = videos[2] 271 video1 = data[2]
293 video2 = videos[3] 272 video2 = data[3]
294 } else { 273 } else {
295 video1 = videos[3] 274 video1 = data[3]
296 video2 = videos[2] 275 video2 = data[2]
297 } 276 }
298 277
299 const checkAttributesVideo1 = { 278 const checkAttributesVideo1 = {
@@ -328,7 +307,7 @@ describe('Test multiple servers', function () {
328 } 307 }
329 ] 308 ]
330 } 309 }
331 await completeVideoCheck(server.url, video1, checkAttributesVideo1) 310 await completeVideoCheck(server, video1, checkAttributesVideo1)
332 311
333 const checkAttributesVideo2 = { 312 const checkAttributesVideo2 = {
334 name: 'my super name for server 3-2', 313 name: 'my super name for server 3-2',
@@ -362,38 +341,38 @@ describe('Test multiple servers', function () {
362 } 341 }
363 ] 342 ]
364 } 343 }
365 await completeVideoCheck(server.url, video2, checkAttributesVideo2) 344 await completeVideoCheck(server, video2, checkAttributesVideo2)
366 } 345 }
367 }) 346 })
368 }) 347 })
369 348
370 describe('It should list local videos', function () { 349 describe('It should list local videos', function () {
371 it('Should list only local videos on server 1', async function () { 350 it('Should list only local videos on server 1', async function () {
372 const { body } = await getLocalVideos(servers[0].url) 351 const { data, total } = await servers[0].videos.list({ filter: 'local' })
373 352
374 expect(body.total).to.equal(1) 353 expect(total).to.equal(1)
375 expect(body.data).to.be.an('array') 354 expect(data).to.be.an('array')
376 expect(body.data.length).to.equal(1) 355 expect(data.length).to.equal(1)
377 expect(body.data[0].name).to.equal('my super name for server 1') 356 expect(data[0].name).to.equal('my super name for server 1')
378 }) 357 })
379 358
380 it('Should list only local videos on server 2', async function () { 359 it('Should list only local videos on server 2', async function () {
381 const { body } = await getLocalVideos(servers[1].url) 360 const { data, total } = await servers[1].videos.list({ filter: 'local' })
382 361
383 expect(body.total).to.equal(1) 362 expect(total).to.equal(1)
384 expect(body.data).to.be.an('array') 363 expect(data).to.be.an('array')
385 expect(body.data.length).to.equal(1) 364 expect(data.length).to.equal(1)
386 expect(body.data[0].name).to.equal('my super name for server 2') 365 expect(data[0].name).to.equal('my super name for server 2')
387 }) 366 })
388 367
389 it('Should list only local videos on server 3', async function () { 368 it('Should list only local videos on server 3', async function () {
390 const { body } = await getLocalVideos(servers[2].url) 369 const { data, total } = await servers[2].videos.list({ filter: 'local' })
391 370
392 expect(body.total).to.equal(2) 371 expect(total).to.equal(2)
393 expect(body.data).to.be.an('array') 372 expect(data).to.be.an('array')
394 expect(body.data.length).to.equal(2) 373 expect(data.length).to.equal(2)
395 expect(body.data[0].name).to.equal('my super name for server 3') 374 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') 375 expect(data[1].name).to.equal('my super name for server 3-2')
397 }) 376 })
398 }) 377 })
399 378
@@ -401,15 +380,13 @@ describe('Test multiple servers', function () {
401 it('Should add the file 1 by asking server 3', async function () { 380 it('Should add the file 1 by asking server 3', async function () {
402 this.timeout(10000) 381 this.timeout(10000)
403 382
404 const res = await getVideosList(servers[2].url) 383 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 384
410 const res2 = await getVideo(servers[2].url, video.id) 385 const video = data[0]
411 const videoDetails = res2.body 386 toRemove.push(data[2])
387 toRemove.push(data[3])
412 388
389 const videoDetails = await servers[2].videos.get({ id: video.id })
413 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true) 390 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
414 expect(torrent.files).to.be.an('array') 391 expect(torrent.files).to.be.an('array')
415 expect(torrent.files.length).to.equal(1) 392 expect(torrent.files.length).to.equal(1)
@@ -419,11 +396,10 @@ describe('Test multiple servers', function () {
419 it('Should add the file 2 by asking server 1', async function () { 396 it('Should add the file 2 by asking server 1', async function () {
420 this.timeout(10000) 397 this.timeout(10000)
421 398
422 const res = await getVideosList(servers[0].url) 399 const { data } = await servers[0].videos.list()
423 400
424 const video = res.body.data[1] 401 const video = data[1]
425 const res2 = await getVideo(servers[0].url, video.id) 402 const videoDetails = await servers[0].videos.get({ id: video.id })
426 const videoDetails = res2.body
427 403
428 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true) 404 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
429 expect(torrent.files).to.be.an('array') 405 expect(torrent.files).to.be.an('array')
@@ -434,11 +410,10 @@ describe('Test multiple servers', function () {
434 it('Should add the file 3 by asking server 2', async function () { 410 it('Should add the file 3 by asking server 2', async function () {
435 this.timeout(10000) 411 this.timeout(10000)
436 412
437 const res = await getVideosList(servers[1].url) 413 const { data } = await servers[1].videos.list()
438 414
439 const video = res.body.data[2] 415 const video = data[2]
440 const res2 = await getVideo(servers[1].url, video.id) 416 const videoDetails = await servers[1].videos.get({ id: video.id })
441 const videoDetails = res2.body
442 417
443 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true) 418 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
444 expect(torrent.files).to.be.an('array') 419 expect(torrent.files).to.be.an('array')
@@ -449,11 +424,10 @@ describe('Test multiple servers', function () {
449 it('Should add the file 3-2 by asking server 1', async function () { 424 it('Should add the file 3-2 by asking server 1', async function () {
450 this.timeout(10000) 425 this.timeout(10000)
451 426
452 const res = await getVideosList(servers[0].url) 427 const { data } = await servers[0].videos.list()
453 428
454 const video = res.body.data[3] 429 const video = data[3]
455 const res2 = await getVideo(servers[0].url, video.id) 430 const videoDetails = await servers[0].videos.get({ id: video.id })
456 const videoDetails = res2.body
457 431
458 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri) 432 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
459 expect(torrent.files).to.be.an('array') 433 expect(torrent.files).to.be.an('array')
@@ -464,11 +438,10 @@ describe('Test multiple servers', function () {
464 it('Should add the file 2 in 360p by asking server 1', async function () { 438 it('Should add the file 2 in 360p by asking server 1', async function () {
465 this.timeout(10000) 439 this.timeout(10000)
466 440
467 const res = await getVideosList(servers[0].url) 441 const { data } = await servers[0].videos.list()
468 442
469 const video = res.body.data.find(v => v.name === 'my super name for server 2') 443 const video = data.find(v => v.name === 'my super name for server 2')
470 const res2 = await getVideo(servers[0].url, video.id) 444 const videoDetails = await servers[0].videos.get({ id: video.id })
471 const videoDetails = res2.body
472 445
473 const file = videoDetails.files.find(f => f.resolution.id === 360) 446 const file = videoDetails.files.find(f => f.resolution.id === 360)
474 expect(file).not.to.be.undefined 447 expect(file).not.to.be.undefined
@@ -487,30 +460,36 @@ describe('Test multiple servers', function () {
487 let remoteVideosServer3 = [] 460 let remoteVideosServer3 = []
488 461
489 before(async function () { 462 before(async function () {
490 const res1 = await getVideosList(servers[0].url) 463 {
491 remoteVideosServer1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 464 const { data } = await servers[0].videos.list()
465 remoteVideosServer1 = data.filter(video => video.isLocal === false).map(video => video.uuid)
466 }
492 467
493 const res2 = await getVideosList(servers[1].url) 468 {
494 remoteVideosServer2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 469 const { data } = await servers[1].videos.list()
470 remoteVideosServer2 = data.filter(video => video.isLocal === false).map(video => video.uuid)
471 }
495 472
496 const res3 = await getVideosList(servers[2].url) 473 {
497 localVideosServer3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid) 474 const { data } = await servers[2].videos.list()
498 remoteVideosServer3 = res3.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 475 localVideosServer3 = data.filter(video => video.isLocal === true).map(video => video.uuid)
476 remoteVideosServer3 = data.filter(video => video.isLocal === false).map(video => video.uuid)
477 }
499 }) 478 })
500 479
501 it('Should view multiple videos on owned servers', async function () { 480 it('Should view multiple videos on owned servers', async function () {
502 this.timeout(30000) 481 this.timeout(30000)
503 482
504 await viewVideo(servers[2].url, localVideosServer3[0]) 483 await servers[2].videos.view({ id: localVideosServer3[0] })
505 await wait(1000) 484 await wait(1000)
506 485
507 await viewVideo(servers[2].url, localVideosServer3[0]) 486 await servers[2].videos.view({ id: localVideosServer3[0] })
508 await viewVideo(servers[2].url, localVideosServer3[1]) 487 await servers[2].videos.view({ id: localVideosServer3[1] })
509 488
510 await wait(1000) 489 await wait(1000)
511 490
512 await viewVideo(servers[2].url, localVideosServer3[0]) 491 await servers[2].videos.view({ id: localVideosServer3[0] })
513 await viewVideo(servers[2].url, localVideosServer3[0]) 492 await servers[2].videos.view({ id: localVideosServer3[0] })
514 493
515 await waitJobs(servers) 494 await waitJobs(servers)
516 495
@@ -520,11 +499,10 @@ describe('Test multiple servers', function () {
520 await waitJobs(servers) 499 await waitJobs(servers)
521 500
522 for (const server of servers) { 501 for (const server of servers) {
523 const res = await getVideosList(server.url) 502 const { data } = await server.videos.list()
524 503
525 const videos = res.body.data 504 const video0 = data.find(v => v.uuid === localVideosServer3[0])
526 const video0 = videos.find(v => v.uuid === localVideosServer3[0]) 505 const video1 = data.find(v => v.uuid === localVideosServer3[1])
527 const video1 = videos.find(v => v.uuid === localVideosServer3[1])
528 506
529 expect(video0.views).to.equal(3) 507 expect(video0.views).to.equal(3)
530 expect(video1.views).to.equal(1) 508 expect(video1.views).to.equal(1)
@@ -535,16 +513,16 @@ describe('Test multiple servers', function () {
535 this.timeout(45000) 513 this.timeout(45000)
536 514
537 const tasks: Promise<any>[] = [] 515 const tasks: Promise<any>[] = []
538 tasks.push(viewVideo(servers[0].url, remoteVideosServer1[0])) 516 tasks.push(servers[0].videos.view({ id: remoteVideosServer1[0] }))
539 tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0])) 517 tasks.push(servers[1].videos.view({ id: remoteVideosServer2[0] }))
540 tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0])) 518 tasks.push(servers[1].videos.view({ id: remoteVideosServer2[0] }))
541 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[0])) 519 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[0] }))
542 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1])) 520 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[1] }))
543 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1])) 521 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[1] }))
544 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1])) 522 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[1] }))
545 tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) 523 tasks.push(servers[2].videos.view({ id: localVideosServer3[1] }))
546 tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) 524 tasks.push(servers[2].videos.view({ id: localVideosServer3[1] }))
547 tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) 525 tasks.push(servers[2].videos.view({ id: localVideosServer3[1] }))
548 526
549 await Promise.all(tasks) 527 await Promise.all(tasks)
550 528
@@ -558,18 +536,16 @@ describe('Test multiple servers', function () {
558 let baseVideos = null 536 let baseVideos = null
559 537
560 for (const server of servers) { 538 for (const server of servers) {
561 const res = await getVideosList(server.url) 539 const { data } = await server.videos.list()
562
563 const videos = res.body.data
564 540
565 // Initialize base videos for future comparisons 541 // Initialize base videos for future comparisons
566 if (baseVideos === null) { 542 if (baseVideos === null) {
567 baseVideos = videos 543 baseVideos = data
568 continue 544 continue
569 } 545 }
570 546
571 for (const baseVideo of baseVideos) { 547 for (const baseVideo of baseVideos) {
572 const sameVideo = videos.find(video => video.name === baseVideo.name) 548 const sameVideo = data.find(video => video.name === baseVideo.name)
573 expect(baseVideo.views).to.equal(sameVideo.views) 549 expect(baseVideo.views).to.equal(sameVideo.views)
574 } 550 }
575 } 551 }
@@ -578,35 +554,34 @@ describe('Test multiple servers', function () {
578 it('Should like and dislikes videos on different services', async function () { 554 it('Should like and dislikes videos on different services', async function () {
579 this.timeout(50000) 555 this.timeout(50000)
580 556
581 await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') 557 await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' })
582 await wait(500) 558 await wait(500)
583 await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike') 559 await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'dislike' })
584 await wait(500) 560 await wait(500)
585 await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') 561 await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' })
586 await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like') 562 await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'like' })
587 await wait(500) 563 await wait(500)
588 await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike') 564 await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'dislike' })
589 await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike') 565 await servers[2].videos.rate({ id: remoteVideosServer3[1], rating: 'dislike' })
590 await wait(500) 566 await wait(500)
591 await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like') 567 await servers[2].videos.rate({ id: remoteVideosServer3[0], rating: 'like' })
592 568
593 await waitJobs(servers) 569 await waitJobs(servers)
594 await wait(5000) 570 await wait(5000)
571 await waitJobs(servers)
595 572
596 let baseVideos = null 573 let baseVideos = null
597 for (const server of servers) { 574 for (const server of servers) {
598 const res = await getVideosList(server.url) 575 const { data } = await server.videos.list()
599
600 const videos = res.body.data
601 576
602 // Initialize base videos for future comparisons 577 // Initialize base videos for future comparisons
603 if (baseVideos === null) { 578 if (baseVideos === null) {
604 baseVideos = videos 579 baseVideos = data
605 continue 580 continue
606 } 581 }
607 582
608 for (const baseVideo of baseVideos) { 583 for (const baseVideo of baseVideos) {
609 const sameVideo = videos.find(video => video.name === baseVideo.name) 584 const sameVideo = data.find(video => video.name === baseVideo.name)
610 expect(baseVideo.likes).to.equal(sameVideo.likes) 585 expect(baseVideo.likes).to.equal(sameVideo.likes)
611 expect(baseVideo.dislikes).to.equal(sameVideo.dislikes) 586 expect(baseVideo.dislikes).to.equal(sameVideo.dislikes)
612 } 587 }
@@ -632,7 +607,7 @@ describe('Test multiple servers', function () {
632 previewfile: 'preview.jpg' 607 previewfile: 'preview.jpg'
633 } 608 }
634 609
635 await updateVideo(servers[2].url, servers[2].accessToken, toRemove[0].id, attributes) 610 await servers[2].videos.update({ id: toRemove[0].id, attributes })
636 611
637 await waitJobs(servers) 612 await waitJobs(servers)
638 }) 613 })
@@ -641,10 +616,9 @@ describe('Test multiple servers', function () {
641 this.timeout(10000) 616 this.timeout(10000)
642 617
643 for (const server of servers) { 618 for (const server of servers) {
644 const res = await getVideosList(server.url) 619 const { data } = await server.videos.list()
645 620
646 const videos = res.body.data 621 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 622 expect(!!videoUpdated).to.be.true
649 623
650 const isLocal = server.url === 'http://localhost:' + servers[2].port 624 const isLocal = server.url === 'http://localhost:' + servers[2].port
@@ -683,49 +657,46 @@ describe('Test multiple servers', function () {
683 thumbnailfile: 'thumbnail', 657 thumbnailfile: 'thumbnail',
684 previewfile: 'preview' 658 previewfile: 'preview'
685 } 659 }
686 await completeVideoCheck(server.url, videoUpdated, checkAttributes) 660 await completeVideoCheck(server, videoUpdated, checkAttributes)
687 } 661 }
688 }) 662 })
689 663
690 it('Should remove the videos 3 and 3-2 by asking server 3', async function () { 664 it('Should remove the videos 3 and 3-2 by asking server 3', async function () {
691 this.timeout(10000) 665 this.timeout(10000)
692 666
693 await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id) 667 await servers[2].videos.remove({ id: toRemove[0].id })
694 await removeVideo(servers[2].url, servers[2].accessToken, toRemove[1].id) 668 await servers[2].videos.remove({ id: toRemove[1].id })
695 669
696 await waitJobs(servers) 670 await waitJobs(servers)
697 }) 671 })
698 672
699 it('Should not have files of videos 3 and 3-2 on each server', async function () { 673 it('Should not have files of videos 3 and 3-2 on each server', async function () {
700 for (const server of servers) { 674 for (const server of servers) {
701 await checkVideoFilesWereRemoved(toRemove[0].uuid, server.internalServerNumber) 675 await checkVideoFilesWereRemoved(toRemove[0].uuid, server)
702 await checkVideoFilesWereRemoved(toRemove[1].uuid, server.internalServerNumber) 676 await checkVideoFilesWereRemoved(toRemove[1].uuid, server)
703 } 677 }
704 }) 678 })
705 679
706 it('Should have videos 1 and 3 on each server', async function () { 680 it('Should have videos 1 and 3 on each server', async function () {
707 for (const server of servers) { 681 for (const server of servers) {
708 const res = await getVideosList(server.url) 682 const { data } = await server.videos.list()
709 683
710 const videos = res.body.data 684 expect(data).to.be.an('array')
711 expect(videos).to.be.an('array') 685 expect(data.length).to.equal(2)
712 expect(videos.length).to.equal(2) 686 expect(data[0].name).not.to.equal(data[1].name)
713 expect(videos[0].name).not.to.equal(videos[1].name) 687 expect(data[0].name).not.to.equal(toRemove[0].name)
714 expect(videos[0].name).not.to.equal(toRemove[0].name) 688 expect(data[1].name).not.to.equal(toRemove[0].name)
715 expect(videos[1].name).not.to.equal(toRemove[0].name) 689 expect(data[0].name).not.to.equal(toRemove[1].name)
716 expect(videos[0].name).not.to.equal(toRemove[1].name) 690 expect(data[1].name).not.to.equal(toRemove[1].name)
717 expect(videos[1].name).not.to.equal(toRemove[1].name) 691
718 692 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 } 693 }
721 }) 694 })
722 695
723 it('Should get the same video by UUID on each server', async function () { 696 it('Should get the same video by UUID on each server', async function () {
724 let baseVideo = null 697 let baseVideo = null
725 for (const server of servers) { 698 for (const server of servers) {
726 const res = await getVideo(server.url, videoUUID) 699 const video = await server.videos.get({ id: videoUUID })
727
728 const video = res.body
729 700
730 if (baseVideo === null) { 701 if (baseVideo === null) {
731 baseVideo = video 702 baseVideo = video
@@ -748,8 +719,7 @@ describe('Test multiple servers', function () {
748 719
749 it('Should get the preview from each server', async function () { 720 it('Should get the preview from each server', async function () {
750 for (const server of servers) { 721 for (const server of servers) {
751 const res = await getVideo(server.url, videoUUID) 722 const video = await server.videos.get({ id: videoUUID })
752 const video = res.body
753 723
754 await testImage(server.url, 'video_short1-preview.webm', video.previewPath) 724 await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
755 } 725 }
@@ -764,36 +734,36 @@ describe('Test multiple servers', function () {
764 734
765 { 735 {
766 const text = 'my super first comment' 736 const text = 'my super first comment'
767 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, text) 737 await servers[0].comments.createThread({ videoId: videoUUID, text })
768 } 738 }
769 739
770 { 740 {
771 const text = 'my super second comment' 741 const text = 'my super second comment'
772 await addVideoCommentThread(servers[2].url, servers[2].accessToken, videoUUID, text) 742 await servers[2].comments.createThread({ videoId: videoUUID, text })
773 } 743 }
774 744
775 await waitJobs(servers) 745 await waitJobs(servers)
776 746
777 { 747 {
778 const threadId = await findCommentId(servers[1].url, videoUUID, 'my super first comment') 748 const threadId = await servers[1].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' })
779 749
780 const text = 'my super answer to thread 1' 750 const text = 'my super answer to thread 1'
781 await addVideoCommentReply(servers[1].url, servers[1].accessToken, videoUUID, threadId, text) 751 await servers[1].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text })
782 } 752 }
783 753
784 await waitJobs(servers) 754 await waitJobs(servers)
785 755
786 { 756 {
787 const threadId = await findCommentId(servers[2].url, videoUUID, 'my super first comment') 757 const threadId = await servers[2].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' })
788 758
789 const res2 = await getVideoThreadComments(servers[2].url, videoUUID, threadId) 759 const body = await servers[2].comments.getThread({ videoId: videoUUID, threadId })
790 const childCommentId = res2.body.children[0].comment.id 760 const childCommentId = body.children[0].comment.id
791 761
792 const text3 = 'my second answer to thread 1' 762 const text3 = 'my second answer to thread 1'
793 await addVideoCommentReply(servers[2].url, servers[2].accessToken, videoUUID, threadId, text3) 763 await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text: text3 })
794 764
795 const text2 = 'my super answer to answer of thread 1' 765 const text2 = 'my super answer to answer of thread 1'
796 await addVideoCommentReply(servers[2].url, servers[2].accessToken, videoUUID, childCommentId, text2) 766 await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: childCommentId, text: text2 })
797 } 767 }
798 768
799 await waitJobs(servers) 769 await waitJobs(servers)
@@ -801,14 +771,14 @@ describe('Test multiple servers', function () {
801 771
802 it('Should have these threads', async function () { 772 it('Should have these threads', async function () {
803 for (const server of servers) { 773 for (const server of servers) {
804 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 774 const body = await server.comments.listThreads({ videoId: videoUUID })
805 775
806 expect(res.body.total).to.equal(2) 776 expect(body.total).to.equal(2)
807 expect(res.body.data).to.be.an('array') 777 expect(body.data).to.be.an('array')
808 expect(res.body.data).to.have.lengthOf(2) 778 expect(body.data).to.have.lengthOf(2)
809 779
810 { 780 {
811 const comment: VideoComment = res.body.data.find(c => c.text === 'my super first comment') 781 const comment = body.data.find(c => c.text === 'my super first comment')
812 expect(comment).to.not.be.undefined 782 expect(comment).to.not.be.undefined
813 expect(comment.inReplyToCommentId).to.be.null 783 expect(comment.inReplyToCommentId).to.be.null
814 expect(comment.account.name).to.equal('root') 784 expect(comment.account.name).to.equal('root')
@@ -819,7 +789,7 @@ describe('Test multiple servers', function () {
819 } 789 }
820 790
821 { 791 {
822 const comment: VideoComment = res.body.data.find(c => c.text === 'my super second comment') 792 const comment = body.data.find(c => c.text === 'my super second comment')
823 expect(comment).to.not.be.undefined 793 expect(comment).to.not.be.undefined
824 expect(comment.inReplyToCommentId).to.be.null 794 expect(comment.inReplyToCommentId).to.be.null
825 expect(comment.account.name).to.equal('root') 795 expect(comment.account.name).to.equal('root')
@@ -833,12 +803,11 @@ describe('Test multiple servers', function () {
833 803
834 it('Should have these comments', async function () { 804 it('Should have these comments', async function () {
835 for (const server of servers) { 805 for (const server of servers) {
836 const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 806 const body = await server.comments.listThreads({ videoId: videoUUID })
837 const threadId = res1.body.data.find(c => c.text === 'my super first comment').id 807 const threadId = body.data.find(c => c.text === 'my super first comment').id
838 808
839 const res2 = await getVideoThreadComments(server.url, videoUUID, threadId) 809 const tree = await server.comments.getThread({ videoId: videoUUID, threadId })
840 810
841 const tree: VideoCommentThreadTree = res2.body
842 expect(tree.comment.text).equal('my super first comment') 811 expect(tree.comment.text).equal('my super first comment')
843 expect(tree.comment.account.name).equal('root') 812 expect(tree.comment.account.name).equal('root')
844 expect(tree.comment.account.host).equal('localhost:' + servers[0].port) 813 expect(tree.comment.account.host).equal('localhost:' + servers[0].port)
@@ -867,19 +836,17 @@ describe('Test multiple servers', function () {
867 it('Should delete a reply', async function () { 836 it('Should delete a reply', async function () {
868 this.timeout(10000) 837 this.timeout(10000)
869 838
870 await deleteVideoComment(servers[2].url, servers[2].accessToken, videoUUID, childOfFirstChild.comment.id) 839 await servers[2].comments.delete({ videoId: videoUUID, commentId: childOfFirstChild.comment.id })
871 840
872 await waitJobs(servers) 841 await waitJobs(servers)
873 }) 842 })
874 843
875 it('Should have this comment marked as deleted', async function () { 844 it('Should have this comment marked as deleted', async function () {
876 for (const server of servers) { 845 for (const server of servers) {
877 const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 846 const { data } = await server.comments.listThreads({ videoId: videoUUID })
878 const threadId = res1.body.data.find(c => c.text === 'my super first comment').id 847 const threadId = data.find(c => c.text === 'my super first comment').id
879
880 const res2 = await getVideoThreadComments(server.url, videoUUID, threadId)
881 848
882 const tree: VideoCommentThreadTree = res2.body 849 const tree = await server.comments.getThread({ videoId: videoUUID, threadId })
883 expect(tree.comment.text).equal('my super first comment') 850 expect(tree.comment.text).equal('my super first comment')
884 851
885 const firstChild = tree.children[0] 852 const firstChild = tree.children[0]
@@ -900,23 +867,23 @@ describe('Test multiple servers', function () {
900 it('Should delete the thread comments', async function () { 867 it('Should delete the thread comments', async function () {
901 this.timeout(10000) 868 this.timeout(10000)
902 869
903 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 5) 870 const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
904 const threadId = res.body.data.find(c => c.text === 'my super first comment').id 871 const commentId = data.find(c => c.text === 'my super first comment').id
905 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId) 872 await servers[0].comments.delete({ videoId: videoUUID, commentId })
906 873
907 await waitJobs(servers) 874 await waitJobs(servers)
908 }) 875 })
909 876
910 it('Should have the threads marked as deleted on other servers too', async function () { 877 it('Should have the threads marked as deleted on other servers too', async function () {
911 for (const server of servers) { 878 for (const server of servers) {
912 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 879 const body = await server.comments.listThreads({ videoId: videoUUID })
913 880
914 expect(res.body.total).to.equal(2) 881 expect(body.total).to.equal(2)
915 expect(res.body.data).to.be.an('array') 882 expect(body.data).to.be.an('array')
916 expect(res.body.data).to.have.lengthOf(2) 883 expect(body.data).to.have.lengthOf(2)
917 884
918 { 885 {
919 const comment: VideoComment = res.body.data[0] 886 const comment = body.data[0]
920 expect(comment).to.not.be.undefined 887 expect(comment).to.not.be.undefined
921 expect(comment.inReplyToCommentId).to.be.null 888 expect(comment.inReplyToCommentId).to.be.null
922 expect(comment.account.name).to.equal('root') 889 expect(comment.account.name).to.equal('root')
@@ -927,7 +894,7 @@ describe('Test multiple servers', function () {
927 } 894 }
928 895
929 { 896 {
930 const deletedComment: VideoComment = res.body.data[1] 897 const deletedComment = body.data[1]
931 expect(deletedComment).to.not.be.undefined 898 expect(deletedComment).to.not.be.undefined
932 expect(deletedComment.isDeleted).to.be.true 899 expect(deletedComment.isDeleted).to.be.true
933 expect(deletedComment.deletedAt).to.not.be.null 900 expect(deletedComment.deletedAt).to.not.be.null
@@ -945,22 +912,22 @@ describe('Test multiple servers', function () {
945 it('Should delete a remote thread by the origin server', async function () { 912 it('Should delete a remote thread by the origin server', async function () {
946 this.timeout(5000) 913 this.timeout(5000)
947 914
948 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 5) 915 const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
949 const threadId = res.body.data.find(c => c.text === 'my super second comment').id 916 const commentId = data.find(c => c.text === 'my super second comment').id
950 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId) 917 await servers[0].comments.delete({ videoId: videoUUID, commentId })
951 918
952 await waitJobs(servers) 919 await waitJobs(servers)
953 }) 920 })
954 921
955 it('Should have the threads marked as deleted on other servers too', async function () { 922 it('Should have the threads marked as deleted on other servers too', async function () {
956 for (const server of servers) { 923 for (const server of servers) {
957 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 924 const body = await server.comments.listThreads({ videoId: videoUUID })
958 925
959 expect(res.body.total).to.equal(2) 926 expect(body.total).to.equal(2)
960 expect(res.body.data).to.have.lengthOf(2) 927 expect(body.data).to.have.lengthOf(2)
961 928
962 { 929 {
963 const comment: VideoComment = res.body.data[0] 930 const comment = body.data[0]
964 expect(comment.text).to.equal('') 931 expect(comment.text).to.equal('')
965 expect(comment.isDeleted).to.be.true 932 expect(comment.isDeleted).to.be.true
966 expect(comment.createdAt).to.not.be.null 933 expect(comment.createdAt).to.not.be.null
@@ -970,7 +937,7 @@ describe('Test multiple servers', function () {
970 } 937 }
971 938
972 { 939 {
973 const comment: VideoComment = res.body.data[1] 940 const comment = body.data[1]
974 expect(comment.text).to.equal('') 941 expect(comment.text).to.equal('')
975 expect(comment.isDeleted).to.be.true 942 expect(comment.isDeleted).to.be.true
976 expect(comment.createdAt).to.not.be.null 943 expect(comment.createdAt).to.not.be.null
@@ -989,17 +956,17 @@ describe('Test multiple servers', function () {
989 downloadEnabled: false 956 downloadEnabled: false
990 } 957 }
991 958
992 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, attributes) 959 await servers[0].videos.update({ id: videoUUID, attributes })
993 960
994 await waitJobs(servers) 961 await waitJobs(servers)
995 962
996 for (const server of servers) { 963 for (const server of servers) {
997 const res = await getVideo(server.url, videoUUID) 964 const video = await server.videos.get({ id: videoUUID })
998 expect(res.body.commentsEnabled).to.be.false 965 expect(video.commentsEnabled).to.be.false
999 expect(res.body.downloadEnabled).to.be.false 966 expect(video.downloadEnabled).to.be.false
1000 967
1001 const text = 'my super forbidden comment' 968 const text = 'my super forbidden comment'
1002 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text, HttpStatusCode.CONFLICT_409) 969 await server.comments.createThread({ videoId: videoUUID, text, expectedStatus: HttpStatusCode.CONFLICT_409 })
1003 } 970 }
1004 }) 971 })
1005 }) 972 })
@@ -1024,8 +991,8 @@ describe('Test multiple servers', function () {
1024 await waitJobs(servers) 991 await waitJobs(servers)
1025 992
1026 for (const server of servers) { 993 for (const server of servers) {
1027 const res = await getVideosList(server.url) 994 const { data } = await server.videos.list()
1028 const video = res.body.data.find(v => v.name === 'minimum parameters') 995 const video = data.find(v => v.name === 'minimum parameters')
1029 996
1030 const isLocal = server.url === 'http://localhost:' + servers[1].port 997 const isLocal = server.url === 'http://localhost:' + servers[1].port
1031 const checkAttributes = { 998 const checkAttributes = {
@@ -1072,7 +1039,7 @@ describe('Test multiple servers', function () {
1072 } 1039 }
1073 ] 1040 ]
1074 } 1041 }
1075 await completeVideoCheck(server.url, video, checkAttributes) 1042 await completeVideoCheck(server, video, checkAttributes)
1076 } 1043 }
1077 }) 1044 })
1078 }) 1045 })
diff --git a/server/tests/api/videos/resumable-upload.ts b/server/tests/api/videos/resumable-upload.ts
index 4fc3317df..13e47c85e 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 })
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index 1058a1e9c..c0535be09 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,122 @@ 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 await server.videos.remove({ id: videoId })
232 203
233 await checkVideoFilesWereRemoved(videoUUID, 1) 204 await checkVideoFilesWereRemoved(videoUUID, server)
234 }) 205 })
235 206
236 it('Should not have videos', async function () { 207 it('Should not have videos', async function () {
237 const res = await getVideosList(server.url) 208 const { total, data } = await server.videos.list()
238 209
239 expect(res.body.total).to.equal(0) 210 expect(total).to.equal(0)
240 expect(res.body.data).to.be.an('array') 211 expect(data).to.be.an('array')
241 expect(res.body.data).to.have.lengthOf(0) 212 expect(data).to.have.lengthOf(0)
242 }) 213 })
243 214
244 it('Should upload 6 videos', async function () { 215 it('Should upload 6 videos', async function () {
@@ -250,7 +221,7 @@ describe('Test a single server', function () {
250 ]) 221 ])
251 222
252 for (const video of videos) { 223 for (const video of videos) {
253 const videoAttributes = { 224 const attributes = {
254 name: video + ' name', 225 name: video + ' name',
255 description: video + ' description', 226 description: video + ' description',
256 category: 2, 227 category: 2,
@@ -261,19 +232,20 @@ describe('Test a single server', function () {
261 fixture: video 232 fixture: video
262 } 233 }
263 234
264 await uploadVideo(server.url, server.accessToken, videoAttributes, HttpStatusCode.OK_200, mode) 235 await server.videos.upload({ attributes, mode })
265 } 236 }
266 }) 237 })
267 238
268 it('Should have the correct durations', async function () { 239 it('Should have the correct durations', async function () {
269 const res = await getVideosList(server.url) 240 const { total, data } = await server.videos.list()
241
242 expect(total).to.equal(6)
243 expect(data).to.be.an('array')
244 expect(data).to.have.lengthOf(6)
270 245
271 expect(res.body.total).to.equal(6) 246 const videosByName: { [ name: string ]: Video } = {}
272 const videos = res.body.data 247 data.forEach(v => { videosByName[v.name] = v })
273 expect(videos).to.be.an('array')
274 expect(videos).to.have.lengthOf(6)
275 248
276 const videosByName = keyBy<{ duration: number }>(videos, 'name')
277 expect(videosByName['video_short.mp4 name'].duration).to.equal(5) 249 expect(videosByName['video_short.mp4 name'].duration).to.equal(5)
278 expect(videosByName['video_short.ogv name'].duration).to.equal(5) 250 expect(videosByName['video_short.ogv name'].duration).to.equal(5)
279 expect(videosByName['video_short.webm name'].duration).to.equal(5) 251 expect(videosByName['video_short.webm name'].duration).to.equal(5)
@@ -283,96 +255,87 @@ describe('Test a single server', function () {
283 }) 255 })
284 256
285 it('Should have the correct thumbnails', async function () { 257 it('Should have the correct thumbnails', async function () {
286 const res = await getVideosList(server.url) 258 const { data } = await server.videos.list()
287 259
288 const videos = res.body.data
289 // For the next test 260 // For the next test
290 videosListBase = videos 261 videosListBase = data
291 262
292 for (const video of videos) { 263 for (const video of data) {
293 const videoName = video.name.replace(' name', '') 264 const videoName = video.name.replace(' name', '')
294 await testImage(server.url, videoName, video.thumbnailPath) 265 await testImage(server.url, videoName, video.thumbnailPath)
295 } 266 }
296 }) 267 })
297 268
298 it('Should list only the two first videos', async function () { 269 it('Should list only the two first videos', async function () {
299 const res = await getVideosListPagination(server.url, 0, 2, 'name') 270 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: 'name' })
300 271
301 const videos = res.body.data 272 expect(total).to.equal(6)
302 expect(res.body.total).to.equal(6) 273 expect(data.length).to.equal(2)
303 expect(videos.length).to.equal(2) 274 expect(data[0].name).to.equal(videosListBase[0].name)
304 expect(videos[0].name).to.equal(videosListBase[0].name) 275 expect(data[1].name).to.equal(videosListBase[1].name)
305 expect(videos[1].name).to.equal(videosListBase[1].name)
306 }) 276 })
307 277
308 it('Should list only the next three videos', async function () { 278 it('Should list only the next three videos', async function () {
309 const res = await getVideosListPagination(server.url, 2, 3, 'name') 279 const { total, data } = await server.videos.list({ start: 2, count: 3, sort: 'name' })
310 280
311 const videos = res.body.data 281 expect(total).to.equal(6)
312 expect(res.body.total).to.equal(6) 282 expect(data.length).to.equal(3)
313 expect(videos.length).to.equal(3) 283 expect(data[0].name).to.equal(videosListBase[2].name)
314 expect(videos[0].name).to.equal(videosListBase[2].name) 284 expect(data[1].name).to.equal(videosListBase[3].name)
315 expect(videos[1].name).to.equal(videosListBase[3].name) 285 expect(data[2].name).to.equal(videosListBase[4].name)
316 expect(videos[2].name).to.equal(videosListBase[4].name)
317 }) 286 })
318 287
319 it('Should list the last video', async function () { 288 it('Should list the last video', async function () {
320 const res = await getVideosListPagination(server.url, 5, 6, 'name') 289 const { total, data } = await server.videos.list({ start: 5, count: 6, sort: 'name' })
321 290
322 const videos = res.body.data 291 expect(total).to.equal(6)
323 expect(res.body.total).to.equal(6) 292 expect(data.length).to.equal(1)
324 expect(videos.length).to.equal(1) 293 expect(data[0].name).to.equal(videosListBase[5].name)
325 expect(videos[0].name).to.equal(videosListBase[5].name)
326 }) 294 })
327 295
328 it('Should not have the total field', async function () { 296 it('Should not have the total field', async function () {
329 const res = await getVideosListPagination(server.url, 5, 6, 'name', true) 297 const { total, data } = await server.videos.list({ start: 5, count: 6, sort: 'name', skipCount: true })
330 298
331 const videos = res.body.data 299 expect(total).to.not.exist
332 expect(res.body.total).to.not.exist 300 expect(data.length).to.equal(1)
333 expect(videos.length).to.equal(1) 301 expect(data[0].name).to.equal(videosListBase[5].name)
334 expect(videos[0].name).to.equal(videosListBase[5].name)
335 }) 302 })
336 303
337 it('Should list and sort by name in descending order', async function () { 304 it('Should list and sort by name in descending order', async function () {
338 const res = await getVideosListSort(server.url, '-name') 305 const { total, data } = await server.videos.list({ sort: '-name' })
339 306
340 const videos = res.body.data 307 expect(total).to.equal(6)
341 expect(res.body.total).to.equal(6) 308 expect(data.length).to.equal(6)
342 expect(videos.length).to.equal(6) 309 expect(data[0].name).to.equal('video_short.webm name')
343 expect(videos[0].name).to.equal('video_short.webm name') 310 expect(data[1].name).to.equal('video_short.ogv name')
344 expect(videos[1].name).to.equal('video_short.ogv name') 311 expect(data[2].name).to.equal('video_short.mp4 name')
345 expect(videos[2].name).to.equal('video_short.mp4 name') 312 expect(data[3].name).to.equal('video_short3.webm name')
346 expect(videos[3].name).to.equal('video_short3.webm name') 313 expect(data[4].name).to.equal('video_short2.webm name')
347 expect(videos[4].name).to.equal('video_short2.webm name') 314 expect(data[5].name).to.equal('video_short1.webm name')
348 expect(videos[5].name).to.equal('video_short1.webm name')
349 315
350 videoId = videos[3].uuid 316 videoId = data[3].uuid
351 videoId2 = videos[5].uuid 317 videoId2 = data[5].uuid
352 }) 318 })
353 319
354 it('Should list and sort by trending in descending order', async function () { 320 it('Should list and sort by trending in descending order', async function () {
355 const res = await getVideosListPagination(server.url, 0, 2, '-trending') 321 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: '-trending' })
356 322
357 const videos = res.body.data 323 expect(total).to.equal(6)
358 expect(res.body.total).to.equal(6) 324 expect(data.length).to.equal(2)
359 expect(videos.length).to.equal(2)
360 }) 325 })
361 326
362 it('Should list and sort by hotness in descending order', async function () { 327 it('Should list and sort by hotness in descending order', async function () {
363 const res = await getVideosListPagination(server.url, 0, 2, '-hot') 328 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: '-hot' })
364 329
365 const videos = res.body.data 330 expect(total).to.equal(6)
366 expect(res.body.total).to.equal(6) 331 expect(data.length).to.equal(2)
367 expect(videos.length).to.equal(2)
368 }) 332 })
369 333
370 it('Should list and sort by best in descending order', async function () { 334 it('Should list and sort by best in descending order', async function () {
371 const res = await getVideosListPagination(server.url, 0, 2, '-best') 335 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: '-best' })
372 336
373 const videos = res.body.data 337 expect(total).to.equal(6)
374 expect(res.body.total).to.equal(6) 338 expect(data.length).to.equal(2)
375 expect(videos.length).to.equal(2)
376 }) 339 })
377 340
378 it('Should update a video', async function () { 341 it('Should update a video', async function () {
@@ -387,67 +350,66 @@ describe('Test a single server', function () {
387 downloadEnabled: false, 350 downloadEnabled: false,
388 tags: [ 'tagup1', 'tagup2' ] 351 tags: [ 'tagup1', 'tagup2' ]
389 } 352 }
390 await updateVideo(server.url, server.accessToken, videoId, attributes) 353 await server.videos.update({ id: videoId, attributes })
391 }) 354 })
392 355
393 it('Should filter by tags and category', async function () { 356 it('Should filter by tags and category', async function () {
394 const res1 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 4 ] }) 357 {
395 expect(res1.body.total).to.equal(1) 358 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') 359 expect(total).to.equal(1)
360 expect(data[0].name).to.equal('my super video updated')
361 }
397 362
398 const res2 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 3 ] }) 363 {
399 expect(res2.body.total).to.equal(0) 364 const { total } = await server.videos.list({ tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 3 ] })
365 expect(total).to.equal(0)
366 }
400 }) 367 })
401 368
402 it('Should have the video updated', async function () { 369 it('Should have the video updated', async function () {
403 this.timeout(60000) 370 this.timeout(60000)
404 371
405 const res = await getVideo(server.url, videoId) 372 const video = await server.videos.get({ id: videoId })
406 const video = res.body
407 373
408 await completeVideoCheck(server.url, video, updateCheckAttributes()) 374 await completeVideoCheck(server, video, updateCheckAttributes())
409 }) 375 })
410 376
411 it('Should update only the tags of a video', async function () { 377 it('Should update only the tags of a video', async function () {
412 const attributes = { 378 const attributes = {
413 tags: [ 'supertag', 'tag1', 'tag2' ] 379 tags: [ 'supertag', 'tag1', 'tag2' ]
414 } 380 }
415 await updateVideo(server.url, server.accessToken, videoId, attributes) 381 await server.videos.update({ id: videoId, attributes })
416 382
417 const res = await getVideo(server.url, videoId) 383 const video = await server.videos.get({ id: videoId })
418 const video = res.body
419 384
420 await completeVideoCheck(server.url, video, Object.assign(updateCheckAttributes(), attributes)) 385 await completeVideoCheck(server, video, Object.assign(updateCheckAttributes(), attributes))
421 }) 386 })
422 387
423 it('Should update only the description of a video', async function () { 388 it('Should update only the description of a video', async function () {
424 const attributes = { 389 const attributes = {
425 description: 'hello everybody' 390 description: 'hello everybody'
426 } 391 }
427 await updateVideo(server.url, server.accessToken, videoId, attributes) 392 await server.videos.update({ id: videoId, attributes })
428 393
429 const res = await getVideo(server.url, videoId) 394 const video = await server.videos.get({ id: videoId })
430 const video = res.body
431 395
432 const expectedAttributes = Object.assign(updateCheckAttributes(), { tags: [ 'supertag', 'tag1', 'tag2' ] }, attributes) 396 const expectedAttributes = Object.assign(updateCheckAttributes(), { tags: [ 'supertag', 'tag1', 'tag2' ] }, attributes)
433 await completeVideoCheck(server.url, video, expectedAttributes) 397 await completeVideoCheck(server, video, expectedAttributes)
434 }) 398 })
435 399
436 it('Should like a video', async function () { 400 it('Should like a video', async function () {
437 await rateVideo(server.url, server.accessToken, videoId, 'like') 401 await server.videos.rate({ id: videoId, rating: 'like' })
438 402
439 const res = await getVideo(server.url, videoId) 403 const video = await server.videos.get({ id: videoId })
440 const video = res.body
441 404
442 expect(video.likes).to.equal(1) 405 expect(video.likes).to.equal(1)
443 expect(video.dislikes).to.equal(0) 406 expect(video.dislikes).to.equal(0)
444 }) 407 })
445 408
446 it('Should dislike the same video', async function () { 409 it('Should dislike the same video', async function () {
447 await rateVideo(server.url, server.accessToken, videoId, 'dislike') 410 await server.videos.rate({ id: videoId, rating: 'dislike' })
448 411
449 const res = await getVideo(server.url, videoId) 412 const video = await server.videos.get({ id: videoId })
450 const video = res.body
451 413
452 expect(video.likes).to.equal(0) 414 expect(video.likes).to.equal(0)
453 expect(video.dislikes).to.equal(1) 415 expect(video.dislikes).to.equal(1)
@@ -457,10 +419,10 @@ describe('Test a single server', function () {
457 { 419 {
458 const now = new Date() 420 const now = new Date()
459 const attributes = { originallyPublishedAt: now.toISOString() } 421 const attributes = { originallyPublishedAt: now.toISOString() }
460 await updateVideo(server.url, server.accessToken, videoId, attributes) 422 await server.videos.update({ id: videoId, attributes })
461 423
462 const res = await getVideosListSort(server.url, '-originallyPublishedAt') 424 const { data } = await server.videos.list({ sort: '-originallyPublishedAt' })
463 const names = res.body.data.map(v => v.name) 425 const names = data.map(v => v.name)
464 426
465 expect(names[0]).to.equal('my super video updated') 427 expect(names[0]).to.equal('my super video updated')
466 expect(names[1]).to.equal('video_short2.webm name') 428 expect(names[1]).to.equal('video_short2.webm name')
@@ -473,10 +435,10 @@ describe('Test a single server', function () {
473 { 435 {
474 const now = new Date() 436 const now = new Date()
475 const attributes = { originallyPublishedAt: now.toISOString() } 437 const attributes = { originallyPublishedAt: now.toISOString() }
476 await updateVideo(server.url, server.accessToken, videoId2, attributes) 438 await server.videos.update({ id: videoId2, attributes })
477 439
478 const res = await getVideosListSort(server.url, '-originallyPublishedAt') 440 const { data } = await server.videos.list({ sort: '-originallyPublishedAt' })
479 const names = res.body.data.map(v => v.name) 441 const names = data.map(v => v.name)
480 442
481 expect(names[0]).to.equal('video_short1.webm name') 443 expect(names[0]).to.equal('video_short1.webm name')
482 expect(names[1]).to.equal('my super video updated') 444 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..4c8e28adf 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,9 @@ 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 await servers[0].videos.remove({ id: videoUUID })
197 182
198 await checkVideoFilesWereRemoved(videoUUID, 1) 183 await checkVideoFilesWereRemoved(videoUUID, servers[0])
199 }) 184 })
200 185
201 after(async function () { 186 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..6ae6d3004 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.video = 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.video.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.video.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.video.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.video.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.video.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.video = 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.video.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..7845f7334 100644
--- a/server/tests/api/videos/video-hls.ts
+++ b/server/tests/api/videos/video-hls.ts
@@ -9,31 +9,22 @@ import {
9 checkSegmentHash, 9 checkSegmentHash,
10 checkTmpIsEmpty, 10 checkTmpIsEmpty,
11 cleanupTests, 11 cleanupTests,
12 createMultipleServers,
12 doubleFollow, 13 doubleFollow,
13 flushAndRunMultipleServers,
14 getPlaylist,
15 getVideo,
16 makeRawRequest, 14 makeRawRequest,
17 removeVideo, 15 PeerTubeServer,
18 ServerInfo,
19 setAccessTokensToServers, 16 setAccessTokensToServers,
20 updateCustomSubConfig,
21 updateVideo,
22 uploadVideo,
23 waitJobs, 17 waitJobs,
24 webtorrentAdd 18 webtorrentAdd
25} from '../../../../shared/extra-utils' 19} from '@shared/extra-utils'
26import { VideoDetails } from '../../../../shared/models/videos' 20import { HttpStatusCode, VideoStreamingPlaylistType } from '@shared/models'
27import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
28import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants' 21import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
29import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
30 22
31const expect = chai.expect 23const expect = chai.expect
32 24
33async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOnly: boolean, resolutions = [ 240, 360, 480, 720 ]) { 25async function checkHlsPlaylist (servers: PeerTubeServer[], videoUUID: string, hlsOnly: boolean, resolutions = [ 240, 360, 480, 720 ]) {
34 for (const server of servers) { 26 for (const server of servers) {
35 const resVideoDetails = await getVideo(server.url, videoUUID) 27 const videoDetails = await server.videos.get({ id: videoUUID })
36 const videoDetails: VideoDetails = resVideoDetails.body
37 const baseUrl = `http://${videoDetails.account.host}` 28 const baseUrl = `http://${videoDetails.account.host}`
38 29
39 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1) 30 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
@@ -68,10 +59,9 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
68 } 59 }
69 60
70 { 61 {
71 await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions) 62 await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
72 63
73 const res = await getPlaylist(hlsPlaylist.playlistUrl) 64 const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl })
74 const masterPlaylist = res.text
75 65
76 for (const resolution of resolutions) { 66 for (const resolution of resolutions) {
77 expect(masterPlaylist).to.contain(`${resolution}.m3u8`) 67 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
@@ -81,9 +71,10 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
81 71
82 { 72 {
83 for (const resolution of resolutions) { 73 for (const resolution of resolutions) {
84 const res = await getPlaylist(`${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${resolution}.m3u8`) 74 const subPlaylist = await server.streamingPlaylists.get({
75 url: `${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${resolution}.m3u8`
76 })
85 77
86 const subPlaylist = res.text
87 expect(subPlaylist).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`) 78 expect(subPlaylist).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`)
88 } 79 }
89 } 80 }
@@ -92,23 +83,31 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
92 const baseUrlAndPath = baseUrl + '/static/streaming-playlists/hls' 83 const baseUrlAndPath = baseUrl + '/static/streaming-playlists/hls'
93 84
94 for (const resolution of resolutions) { 85 for (const resolution of resolutions) {
95 await checkSegmentHash(baseUrlAndPath, baseUrlAndPath, videoUUID, resolution, hlsPlaylist) 86 await checkSegmentHash({
87 server,
88 baseUrlPlaylist: baseUrlAndPath,
89 baseUrlSegment: baseUrlAndPath,
90 videoUUID,
91 resolution,
92 hlsPlaylist
93 })
96 } 94 }
97 } 95 }
98 } 96 }
99} 97}
100 98
101describe('Test HLS videos', function () { 99describe('Test HLS videos', function () {
102 let servers: ServerInfo[] = [] 100 let servers: PeerTubeServer[] = []
103 let videoUUID = '' 101 let videoUUID = ''
104 let videoAudioUUID = '' 102 let videoAudioUUID = ''
105 103
106 function runTestSuite (hlsOnly: boolean) { 104 function runTestSuite (hlsOnly: boolean) {
105
107 it('Should upload a video and transcode it to HLS', async function () { 106 it('Should upload a video and transcode it to HLS', async function () {
108 this.timeout(120000) 107 this.timeout(120000)
109 108
110 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 1', fixture: 'video_short.webm' }) 109 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1', fixture: 'video_short.webm' } })
111 videoUUID = res.body.video.uuid 110 videoUUID = uuid
112 111
113 await waitJobs(servers) 112 await waitJobs(servers)
114 113
@@ -118,8 +117,8 @@ describe('Test HLS videos', function () {
118 it('Should upload an audio file and transcode it to HLS', async function () { 117 it('Should upload an audio file and transcode it to HLS', async function () {
119 this.timeout(120000) 118 this.timeout(120000)
120 119
121 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video audio', fixture: 'sample.ogg' }) 120 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video audio', fixture: 'sample.ogg' } })
122 videoAudioUUID = res.body.video.uuid 121 videoAudioUUID = uuid
123 122
124 await waitJobs(servers) 123 await waitJobs(servers)
125 124
@@ -129,7 +128,7 @@ describe('Test HLS videos', function () {
129 it('Should update the video', async function () { 128 it('Should update the video', async function () {
130 this.timeout(10000) 129 this.timeout(10000)
131 130
132 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video 1 updated' }) 131 await servers[0].videos.update({ id: videoUUID, attributes: { name: 'video 1 updated' } })
133 132
134 await waitJobs(servers) 133 await waitJobs(servers)
135 134
@@ -139,14 +138,14 @@ describe('Test HLS videos', function () {
139 it('Should delete videos', async function () { 138 it('Should delete videos', async function () {
140 this.timeout(10000) 139 this.timeout(10000)
141 140
142 await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) 141 await servers[0].videos.remove({ id: videoUUID })
143 await removeVideo(servers[0].url, servers[0].accessToken, videoAudioUUID) 142 await servers[0].videos.remove({ id: videoAudioUUID })
144 143
145 await waitJobs(servers) 144 await waitJobs(servers)
146 145
147 for (const server of servers) { 146 for (const server of servers) {
148 await getVideo(server.url, videoUUID, HttpStatusCode.NOT_FOUND_404) 147 await server.videos.get({ id: videoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
149 await getVideo(server.url, videoAudioUUID, HttpStatusCode.NOT_FOUND_404) 148 await server.videos.get({ id: videoAudioUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
150 } 149 }
151 }) 150 })
152 151
@@ -176,7 +175,7 @@ describe('Test HLS videos', function () {
176 } 175 }
177 } 176 }
178 } 177 }
179 servers = await flushAndRunMultipleServers(2, configOverride) 178 servers = await createMultipleServers(2, configOverride)
180 179
181 // Get the access tokens 180 // Get the access tokens
182 await setAccessTokensToServers(servers) 181 await setAccessTokensToServers(servers)
@@ -192,24 +191,26 @@ describe('Test HLS videos', function () {
192 describe('With only HLS enabled', function () { 191 describe('With only HLS enabled', function () {
193 192
194 before(async function () { 193 before(async function () {
195 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 194 await servers[0].config.updateCustomSubConfig({
196 transcoding: { 195 newConfig: {
197 enabled: true, 196 transcoding: {
198 allowAudioFiles: true, 197 enabled: true,
199 resolutions: { 198 allowAudioFiles: true,
200 '240p': true, 199 resolutions: {
201 '360p': true, 200 '240p': true,
202 '480p': true, 201 '360p': true,
203 '720p': true, 202 '480p': true,
204 '1080p': true, 203 '720p': true,
205 '1440p': true, 204 '1080p': true,
206 '2160p': true 205 '1440p': true,
207 }, 206 '2160p': true
208 hls: { 207 },
209 enabled: true 208 hls: {
210 }, 209 enabled: true
211 webtorrent: { 210 },
212 enabled: false 211 webtorrent: {
212 enabled: false
213 }
213 } 214 }
214 } 215 }
215 }) 216 })
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..e4892bb24 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' 6import { 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 { 7import {
12 buildAbsoluteFixturePath, 8 buildAbsoluteFixturePath,
13 buildServerDirectory,
14 cleanupTests, 9 cleanupTests,
10 createMultipleServers,
15 doubleFollow, 11 doubleFollow,
16 flushAndRunMultipleServers,
17 generateHighBitrateVideo, 12 generateHighBitrateVideo,
18 generateVideoWithFramerate, 13 generateVideoWithFramerate,
19 getJobsListPaginationAndSort,
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,23 @@ 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 path = servers[1].servers.buildDirectory(join('videos', video.uuid + '-240.mp4'))
290 const probe = await getAudioStream(path) 275 const probe = await getAudioStream(path)
291 276
292 if (probe.audioStream) { 277 if (probe.audioStream) {
@@ -301,23 +286,22 @@ describe('Test video transcoding', function () {
301 it('Should transcode video with no audio and have no audio itself', async function () { 286 it('Should transcode video with no audio and have no audio itself', async function () {
302 this.timeout(60_000) 287 this.timeout(60_000)
303 288
304 const videoAttributes = { 289 const attributes = {
305 name: 'no_audio', 290 name: 'no_audio',
306 fixture: 'video_short_no_audio.mp4' 291 fixture: 'video_short_no_audio.mp4'
307 } 292 }
308 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 293 await servers[1].videos.upload({ attributes })
309 294
310 await waitJobs(servers) 295 await waitJobs(servers)
311 296
312 for (const server of servers) { 297 for (const server of servers) {
313 const res = await getVideosList(server.url) 298 const { data } = await server.videos.list()
314 299
315 const video = res.body.data.find(v => v.name === videoAttributes.name) 300 const video = data.find(v => v.name === attributes.name)
316 const res2 = await getVideo(server.url, video.id) 301 const videoDetails = await server.videos.get({ id: video.id })
317 const videoDetails: VideoDetails = res2.body
318 302
319 expect(videoDetails.files).to.have.lengthOf(4) 303 expect(videoDetails.files).to.have.lengthOf(4)
320 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4')) 304 const path = servers[1].servers.buildDirectory(join('videos', video.uuid + '-240.mp4'))
321 const probe = await getAudioStream(path) 305 const probe = await getAudioStream(path)
322 expect(probe).to.not.have.property('audioStream') 306 expect(probe).to.not.have.property('audioStream')
323 } 307 }
@@ -326,26 +310,25 @@ describe('Test video transcoding', function () {
326 it('Should leave the audio untouched, but properly transcode the video', async function () { 310 it('Should leave the audio untouched, but properly transcode the video', async function () {
327 this.timeout(60_000) 311 this.timeout(60_000)
328 312
329 const videoAttributes = { 313 const attributes = {
330 name: 'untouched_audio', 314 name: 'untouched_audio',
331 fixture: 'video_short.mp4' 315 fixture: 'video_short.mp4'
332 } 316 }
333 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 317 await servers[1].videos.upload({ attributes })
334 318
335 await waitJobs(servers) 319 await waitJobs(servers)
336 320
337 for (const server of servers) { 321 for (const server of servers) {
338 const res = await getVideosList(server.url) 322 const { data } = await server.videos.list()
339 323
340 const video = res.body.data.find(v => v.name === videoAttributes.name) 324 const video = data.find(v => v.name === attributes.name)
341 const res2 = await getVideo(server.url, video.id) 325 const videoDetails = await server.videos.get({ id: video.id })
342 const videoDetails: VideoDetails = res2.body
343 326
344 expect(videoDetails.files).to.have.lengthOf(4) 327 expect(videoDetails.files).to.have.lengthOf(4)
345 328
346 const fixturePath = buildAbsoluteFixturePath(videoAttributes.fixture) 329 const fixturePath = buildAbsoluteFixturePath(attributes.fixture)
347 const fixtureVideoProbe = await getAudioStream(fixturePath) 330 const fixtureVideoProbe = await getAudioStream(fixturePath)
348 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4')) 331 const path = servers[1].servers.buildDirectory(join('videos', video.uuid + '-240.mp4'))
349 332
350 const videoProbe = await getAudioStream(path) 333 const videoProbe = await getAudioStream(path)
351 334
@@ -364,19 +347,21 @@ describe('Test video transcoding', function () {
364 function runSuite (mode: 'legacy' | 'resumable') { 347 function runSuite (mode: 'legacy' | 'resumable') {
365 348
366 before(async function () { 349 before(async function () {
367 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, { 350 await servers[1].config.updateCustomSubConfig({
368 transcoding: { 351 newConfig: {
369 hls: { enabled: true }, 352 transcoding: {
370 webtorrent: { enabled: true }, 353 hls: { enabled: true },
371 resolutions: { 354 webtorrent: { enabled: true },
372 '0p': false, 355 resolutions: {
373 '240p': false, 356 '0p': false,
374 '360p': false, 357 '240p': false,
375 '480p': false, 358 '360p': false,
376 '720p': false, 359 '480p': false,
377 '1080p': false, 360 '720p': false,
378 '1440p': false, 361 '1080p': false,
379 '2160p': false 362 '1440p': false,
363 '2160p': false
364 }
380 } 365 }
381 } 366 }
382 }) 367 })
@@ -385,22 +370,21 @@ describe('Test video transcoding', function () {
385 it('Should merge an audio file with the preview file', async function () { 370 it('Should merge an audio file with the preview file', async function () {
386 this.timeout(60_000) 371 this.timeout(60_000)
387 372
388 const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 373 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) 374 await servers[1].videos.upload({ attributes, mode })
390 375
391 await waitJobs(servers) 376 await waitJobs(servers)
392 377
393 for (const server of servers) { 378 for (const server of servers) {
394 const res = await getVideosList(server.url) 379 const { data } = await server.videos.list()
395 380
396 const video = res.body.data.find(v => v.name === 'audio_with_preview') 381 const video = data.find(v => v.name === 'audio_with_preview')
397 const res2 = await getVideo(server.url, video.id) 382 const videoDetails = await server.videos.get({ id: video.id })
398 const videoDetails: VideoDetails = res2.body
399 383
400 expect(videoDetails.files).to.have.lengthOf(1) 384 expect(videoDetails.files).to.have.lengthOf(1)
401 385
402 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: HttpStatusCode.OK_200 }) 386 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 }) 387 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
404 388
405 const magnetUri = videoDetails.files[0].magnetUri 389 const magnetUri = videoDetails.files[0].magnetUri
406 expect(magnetUri).to.contain('.mp4') 390 expect(magnetUri).to.contain('.mp4')
@@ -410,22 +394,21 @@ describe('Test video transcoding', function () {
410 it('Should upload an audio file and choose a default background image', async function () { 394 it('Should upload an audio file and choose a default background image', async function () {
411 this.timeout(60_000) 395 this.timeout(60_000)
412 396
413 const videoAttributesArg = { name: 'audio_without_preview', fixture: 'sample.ogg' } 397 const attributes = { name: 'audio_without_preview', fixture: 'sample.ogg' }
414 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributesArg, HttpStatusCode.OK_200, mode) 398 await servers[1].videos.upload({ attributes, mode })
415 399
416 await waitJobs(servers) 400 await waitJobs(servers)
417 401
418 for (const server of servers) { 402 for (const server of servers) {
419 const res = await getVideosList(server.url) 403 const { data } = await server.videos.list()
420 404
421 const video = res.body.data.find(v => v.name === 'audio_without_preview') 405 const video = data.find(v => v.name === 'audio_without_preview')
422 const res2 = await getVideo(server.url, video.id) 406 const videoDetails = await server.videos.get({ id: video.id })
423 const videoDetails = res2.body
424 407
425 expect(videoDetails.files).to.have.lengthOf(1) 408 expect(videoDetails.files).to.have.lengthOf(1)
426 409
427 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: HttpStatusCode.OK_200 }) 410 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 }) 411 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
429 412
430 const magnetUri = videoDetails.files[0].magnetUri 413 const magnetUri = videoDetails.files[0].magnetUri
431 expect(magnetUri).to.contain('.mp4') 414 expect(magnetUri).to.contain('.mp4')
@@ -435,26 +418,27 @@ describe('Test video transcoding', function () {
435 it('Should upload an audio file and create an audio version only', async function () { 418 it('Should upload an audio file and create an audio version only', async function () {
436 this.timeout(60_000) 419 this.timeout(60_000)
437 420
438 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, { 421 await servers[1].config.updateCustomSubConfig({
439 transcoding: { 422 newConfig: {
440 hls: { enabled: true }, 423 transcoding: {
441 webtorrent: { enabled: true }, 424 hls: { enabled: true },
442 resolutions: { 425 webtorrent: { enabled: true },
443 '0p': true, 426 resolutions: {
444 '240p': false, 427 '0p': true,
445 '360p': false 428 '240p': false,
429 '360p': false
430 }
446 } 431 }
447 } 432 }
448 }) 433 })
449 434
450 const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 435 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) 436 const { id } = await servers[1].videos.upload({ attributes, mode })
452 437
453 await waitJobs(servers) 438 await waitJobs(servers)
454 439
455 for (const server of servers) { 440 for (const server of servers) {
456 const res2 = await getVideo(server.url, resVideo.body.video.id) 441 const videoDetails = await server.videos.get({ id })
457 const videoDetails: VideoDetails = res2.body
458 442
459 for (const files of [ videoDetails.files, videoDetails.streamingPlaylists[0].files ]) { 443 for (const files of [ videoDetails.files, videoDetails.streamingPlaylists[0].files ]) {
460 expect(files).to.have.lengthOf(2) 444 expect(files).to.have.lengthOf(2)
@@ -480,21 +464,20 @@ describe('Test video transcoding', function () {
480 it('Should transcode a 60 FPS video', async function () { 464 it('Should transcode a 60 FPS video', async function () {
481 this.timeout(60_000) 465 this.timeout(60_000)
482 466
483 const videoAttributes = { 467 const attributes = {
484 name: 'my super 30fps name for server 2', 468 name: 'my super 30fps name for server 2',
485 description: 'my super 30fps description for server 2', 469 description: 'my super 30fps description for server 2',
486 fixture: '60fps_720p_small.mp4' 470 fixture: '60fps_720p_small.mp4'
487 } 471 }
488 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 472 await servers[1].videos.upload({ attributes })
489 473
490 await waitJobs(servers) 474 await waitJobs(servers)
491 475
492 for (const server of servers) { 476 for (const server of servers) {
493 const res = await getVideosList(server.url) 477 const { data } = await server.videos.list()
494 478
495 const video = res.body.data.find(v => v.name === videoAttributes.name) 479 const video = data.find(v => v.name === attributes.name)
496 const res2 = await getVideo(server.url, video.id) 480 const videoDetails = await server.videos.get({ id: video.id })
497 const videoDetails: VideoDetails = res2.body
498 481
499 expect(videoDetails.files).to.have.lengthOf(4) 482 expect(videoDetails.files).to.have.lengthOf(4)
500 expect(videoDetails.files[0].fps).to.be.above(58).and.below(62) 483 expect(videoDetails.files[0].fps).to.be.above(58).and.below(62)
@@ -503,13 +486,13 @@ describe('Test video transcoding', function () {
503 expect(videoDetails.files[3].fps).to.be.below(31) 486 expect(videoDetails.files[3].fps).to.be.below(31)
504 487
505 for (const resolution of [ '240', '360', '480' ]) { 488 for (const resolution of [ '240', '360', '480' ]) {
506 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-' + resolution + '.mp4')) 489 const path = servers[1].servers.buildDirectory(join('videos', video.uuid + '-' + resolution + '.mp4'))
507 const fps = await getVideoFileFPS(path) 490 const fps = await getVideoFileFPS(path)
508 491
509 expect(fps).to.be.below(31) 492 expect(fps).to.be.below(31)
510 } 493 }
511 494
512 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-720.mp4')) 495 const path = servers[1].servers.buildDirectory(join('videos', video.uuid + '-720.mp4'))
513 const fps = await getVideoFileFPS(path) 496 const fps = await getVideoFileFPS(path)
514 497
515 expect(fps).to.be.above(58).and.below(62) 498 expect(fps).to.be.above(58).and.below(62)
@@ -528,29 +511,29 @@ describe('Test video transcoding', function () {
528 expect(fps).to.be.equal(59) 511 expect(fps).to.be.equal(59)
529 } 512 }
530 513
531 const videoAttributes = { 514 const attributes = {
532 name: '59fps video', 515 name: '59fps video',
533 description: '59fps video', 516 description: '59fps video',
534 fixture: tempFixturePath 517 fixture: tempFixturePath
535 } 518 }
536 519
537 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 520 await servers[1].videos.upload({ attributes })
538 521
539 await waitJobs(servers) 522 await waitJobs(servers)
540 523
541 for (const server of servers) { 524 for (const server of servers) {
542 const res = await getVideosList(server.url) 525 const { data } = await server.videos.list()
543 526
544 const video = res.body.data.find(v => v.name === videoAttributes.name) 527 const video = data.find(v => v.name === attributes.name)
545 528
546 { 529 {
547 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4')) 530 const path = servers[1].servers.buildDirectory(join('videos', video.uuid + '-240.mp4'))
548 const fps = await getVideoFileFPS(path) 531 const fps = await getVideoFileFPS(path)
549 expect(fps).to.be.equal(25) 532 expect(fps).to.be.equal(25)
550 } 533 }
551 534
552 { 535 {
553 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-720.mp4')) 536 const path = servers[1].servers.buildDirectory(join('videos', video.uuid + '-720.mp4'))
554 const fps = await getVideoFileFPS(path) 537 const fps = await getVideoFileFPS(path)
555 expect(fps).to.be.equal(59) 538 expect(fps).to.be.equal(59)
556 } 539 }
@@ -571,23 +554,23 @@ describe('Test video transcoding', function () {
571 expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 25, VIDEO_TRANSCODING_FPS)) 554 expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 25, VIDEO_TRANSCODING_FPS))
572 } 555 }
573 556
574 const videoAttributes = { 557 const attributes = {
575 name: 'high bitrate video', 558 name: 'high bitrate video',
576 description: 'high bitrate video', 559 description: 'high bitrate video',
577 fixture: tempFixturePath 560 fixture: tempFixturePath
578 } 561 }
579 562
580 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 563 await servers[1].videos.upload({ attributes })
581 564
582 await waitJobs(servers) 565 await waitJobs(servers)
583 566
584 for (const server of servers) { 567 for (const server of servers) {
585 const res = await getVideosList(server.url) 568 const { data } = await server.videos.list()
586 569
587 const video = res.body.data.find(v => v.name === videoAttributes.name) 570 const video = data.find(v => v.name === attributes.name)
588 571
589 for (const resolution of [ '240', '360', '480', '720', '1080' ]) { 572 for (const resolution of [ '240', '360', '480', '720', '1080' ]) {
590 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-' + resolution + '.mp4')) 573 const path = servers[1].servers.buildDirectory(join('videos', video.uuid + '-' + resolution + '.mp4'))
591 574
592 const bitrate = await getVideoFileBitrate(path) 575 const bitrate = await getVideoFileBitrate(path)
593 const fps = await getVideoFileFPS(path) 576 const fps = await getVideoFileFPS(path)
@@ -602,7 +585,7 @@ describe('Test video transcoding', function () {
602 it('Should not transcode to an higher bitrate than the original file', async function () { 585 it('Should not transcode to an higher bitrate than the original file', async function () {
603 this.timeout(160_000) 586 this.timeout(160_000)
604 587
605 const config = { 588 const newConfig = {
606 transcoding: { 589 transcoding: {
607 enabled: true, 590 enabled: true,
608 resolutions: { 591 resolutions: {
@@ -618,22 +601,21 @@ describe('Test video transcoding', function () {
618 hls: { enabled: true } 601 hls: { enabled: true }
619 } 602 }
620 } 603 }
621 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) 604 await servers[1].config.updateCustomSubConfig({ newConfig })
622 605
623 const videoAttributes = { 606 const attributes = {
624 name: 'low bitrate', 607 name: 'low bitrate',
625 fixture: 'low-bitrate.mp4' 608 fixture: 'low-bitrate.mp4'
626 } 609 }
627 610
628 const resUpload = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 611 const { uuid } = await servers[1].videos.upload({ attributes })
629 const videoUUID = resUpload.body.video.uuid
630 612
631 await waitJobs(servers) 613 await waitJobs(servers)
632 614
633 const resolutions = [ 240, 360, 480, 720, 1080 ] 615 const resolutions = [ 240, 360, 480, 720, 1080 ]
634 for (const r of resolutions) { 616 for (const r of resolutions) {
635 const path = `videos/${videoUUID}-${r}.mp4` 617 const path = `videos/${uuid}-${r}.mp4`
636 const size = await getServerFileSize(servers[1], path) 618 const size = await servers[1].servers.getServerFileSize(path)
637 expect(size, `${path} not below ${60_000}`).to.be.below(60_000) 619 expect(size, `${path} not below ${60_000}`).to.be.below(60_000)
638 } 620 }
639 }) 621 })
@@ -644,11 +626,11 @@ describe('Test video transcoding', function () {
644 it('Should provide valid ffprobe data', async function () { 626 it('Should provide valid ffprobe data', async function () {
645 this.timeout(160_000) 627 this.timeout(160_000)
646 628
647 const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'ffprobe data' })).uuid 629 const videoUUID = (await servers[1].videos.quickUpload({ name: 'ffprobe data' })).uuid
648 await waitJobs(servers) 630 await waitJobs(servers)
649 631
650 { 632 {
651 const path = buildServerDirectory(servers[1], join('videos', videoUUID + '-240.mp4')) 633 const path = servers[1].servers.buildDirectory(join('videos', videoUUID + '-240.mp4'))
652 const metadata = await getMetadataFromFile(path) 634 const metadata = await getMetadataFromFile(path)
653 635
654 // expected format properties 636 // expected format properties
@@ -678,8 +660,7 @@ describe('Test video transcoding', function () {
678 } 660 }
679 661
680 for (const server of servers) { 662 for (const server of servers) {
681 const res2 = await getVideo(server.url, videoUUID) 663 const videoDetails = await server.videos.get({ id: videoUUID })
682 const videoDetails: VideoDetails = res2.body
683 664
684 const videoFiles = videoDetails.files 665 const videoFiles = videoDetails.files
685 .concat(videoDetails.streamingPlaylists[0].files) 666 .concat(videoDetails.streamingPlaylists[0].files)
@@ -691,8 +672,7 @@ describe('Test video transcoding', function () {
691 expect(file.metadataUrl).to.contain(servers[1].url) 672 expect(file.metadataUrl).to.contain(servers[1].url)
692 expect(file.metadataUrl).to.contain(videoUUID) 673 expect(file.metadataUrl).to.contain(videoUUID)
693 674
694 const res3 = await getVideoFileMetadataUrl(file.metadataUrl) 675 const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl })
695 const metadata: FfprobeData = res3.body
696 expect(metadata).to.have.nested.property('format.size') 676 expect(metadata).to.have.nested.property('format.size')
697 } 677 }
698 } 678 }
@@ -709,17 +689,14 @@ describe('Test video transcoding', function () {
709 describe('Transcoding job queue', function () { 689 describe('Transcoding job queue', function () {
710 690
711 it('Should have the appropriate priorities for transcoding jobs', async function () { 691 it('Should have the appropriate priorities for transcoding jobs', async function () {
712 const res = await getJobsListPaginationAndSort({ 692 const body = await servers[1].jobs.getJobsList({
713 url: servers[1].url,
714 accessToken: servers[1].accessToken,
715 start: 0, 693 start: 0,
716 count: 100, 694 count: 100,
717 sort: '-createdAt', 695 sort: '-createdAt',
718 jobType: 'video-transcoding' 696 jobType: 'video-transcoding'
719 }) 697 })
720 698
721 const jobs = res.body.data as Job[] 699 const jobs = body.data
722
723 const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k) 700 const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k)
724 701
725 expect(transcodingJobs).to.have.lengthOf(14) 702 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..685b3b7b8 100644
--- a/server/tests/cli/optimize-old-videos.ts
+++ b/server/tests/cli/optimize-old-videos.ts
@@ -4,36 +4,29 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path' 5import { join } from 'path'
6import { 6import {
7 buildServerDirectory,
8 cleanupTests, 7 cleanupTests,
8 createMultipleServers,
9 doubleFollow, 9 doubleFollow,
10 execCLI,
11 flushAndRunMultipleServers,
12 generateHighBitrateVideo, 10 generateHighBitrateVideo,
13 getEnvCli, 11 PeerTubeServer,
14 getVideo,
15 getVideosList,
16 ServerInfo,
17 setAccessTokensToServers, 12 setAccessTokensToServers,
18 uploadVideo, 13 wait,
19 viewVideo, 14 waitJobs
20 wait 15} from '@shared/extra-utils'
21} from '../../../shared/extra-utils' 16import { 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' 17import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffprobe-utils'
25import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants' 18import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants'
26 19
27const expect = chai.expect 20const expect = chai.expect
28 21
29describe('Test optimize old videos', function () { 22describe('Test optimize old videos', function () {
30 let servers: ServerInfo[] = [] 23 let servers: PeerTubeServer[] = []
31 24
32 before(async function () { 25 before(async function () {
33 this.timeout(200000) 26 this.timeout(200000)
34 27
35 // Run server 2 to have transcoding enabled 28 // Run server 2 to have transcoding enabled
36 servers = await flushAndRunMultipleServers(2) 29 servers = await createMultipleServers(2)
37 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
38 31
39 await doubleFollow(servers[0], servers[1]) 32 await doubleFollow(servers[0], servers[1])
@@ -48,8 +41,8 @@ describe('Test optimize old videos', function () {
48 } 41 }
49 42
50 // Upload two videos for our needs 43 // Upload two videos for our needs
51 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video1', fixture: tempFixturePath }) 44 await servers[0].videos.upload({ attributes: { name: 'video1', fixture: tempFixturePath } })
52 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video2', fixture: tempFixturePath }) 45 await servers[0].videos.upload({ attributes: { name: 'video2', fixture: tempFixturePath } })
53 46
54 await waitJobs(servers) 47 await waitJobs(servers)
55 }) 48 })
@@ -58,14 +51,12 @@ describe('Test optimize old videos', function () {
58 this.timeout(30000) 51 this.timeout(30000)
59 52
60 for (const server of servers) { 53 for (const server of servers) {
61 const res = await getVideosList(server.url) 54 const { data } = await server.videos.list()
62 const videos = res.body.data 55 expect(data).to.have.lengthOf(2)
63 expect(videos).to.have.lengthOf(2) 56
64 57 for (const video of data) {
65 for (const video of videos) { 58 const videoDetails = await server.videos.get({ id: video.uuid })
66 const res2 = await getVideo(server.url, video.uuid) 59 expect(videoDetails.files).to.have.lengthOf(1)
67 const videoDetail: VideoDetails = res2.body
68 expect(videoDetail.files).to.have.lengthOf(1)
69 } 60 }
70 } 61 }
71 }) 62 })
@@ -73,34 +64,29 @@ describe('Test optimize old videos', function () {
73 it('Should run optimize script', async function () { 64 it('Should run optimize script', async function () {
74 this.timeout(200000) 65 this.timeout(200000)
75 66
76 const env = getEnvCli(servers[0]) 67 await servers[0].cli.execWithEnv('npm run optimize-old-videos')
77 await execCLI(`${env} npm run optimize-old-videos`)
78
79 await waitJobs(servers) 68 await waitJobs(servers)
80 69
81 for (const server of servers) { 70 for (const server of servers) {
82 const res = await getVideosList(server.url) 71 const { data } = await server.videos.list()
83 const videos: Video[] = res.body.data 72 expect(data).to.have.lengthOf(2)
84
85 expect(videos).to.have.lengthOf(2)
86 73
87 for (const video of videos) { 74 for (const video of data) {
88 await viewVideo(server.url, video.uuid) 75 await server.videos.view({ id: video.uuid })
89 76
90 // Refresh video 77 // Refresh video
91 await waitJobs(servers) 78 await waitJobs(servers)
92 await wait(5000) 79 await wait(5000)
93 await waitJobs(servers) 80 await waitJobs(servers)
94 81
95 const res2 = await getVideo(server.url, video.uuid) 82 const videoDetails = await server.videos.get({ id: video.uuid })
96 const videosDetails: VideoDetails = res2.body
97 83
98 expect(videosDetails.files).to.have.lengthOf(1) 84 expect(videoDetails.files).to.have.lengthOf(1)
99 const file = videosDetails.files[0] 85 const file = videoDetails.files[0]
100 86
101 expect(file.size).to.be.below(8000000) 87 expect(file.size).to.be.below(8000000)
102 88
103 const path = buildServerDirectory(servers[0], join('videos', video.uuid + '-' + file.resolution.id + '.mp4')) 89 const path = servers[0].servers.buildDirectory(join('videos', video.uuid + '-' + file.resolution.id + '.mp4'))
104 const bitrate = await getVideoFileBitrate(path) 90 const bitrate = await getVideoFileBitrate(path)
105 const fps = await getVideoFileFPS(path) 91 const fps = await getVideoFileFPS(path)
106 const resolution = await getVideoFileResolution(path) 92 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..954a87833 100644
--- a/server/tests/cli/prune-storage.ts
+++ b/server/tests/cli/prune-storage.ts
@@ -5,87 +5,78 @@ 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[]) {
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 }
63} 56}
64 57
65describe('Test prune storage scripts', function () { 58describe('Test prune storage scripts', function () {
66 let servers: ServerInfo[] 59 let servers: PeerTubeServer[]
67 const badNames: { [directory: string]: string[] } = {} 60 const badNames: { [directory: string]: string[] } = {}
68 61
69 before(async function () { 62 before(async function () {
70 this.timeout(120000) 63 this.timeout(120000)
71 64
72 servers = await flushAndRunMultipleServers(2, { transcoding: { enabled: true } }) 65 servers = await createMultipleServers(2, { transcoding: { enabled: true } })
73 await setAccessTokensToServers(servers) 66 await setAccessTokensToServers(servers)
74 await setDefaultVideoChannel(servers) 67 await setDefaultVideoChannel(servers)
75 68
76 for (const server of servers) { 69 for (const server of servers) {
77 await uploadVideo(server.url, server.accessToken, { name: 'video 1' }) 70 await server.videos.upload({ attributes: { name: 'video 1' } })
78 await uploadVideo(server.url, server.accessToken, { name: 'video 2' }) 71 await server.videos.upload({ attributes: { name: 'video 2' } })
79 72
80 await updateMyAvatar({ url: server.url, accessToken: server.accessToken, fixture: 'avatar.png' }) 73 await server.users.updateMyAvatar({ fixture: 'avatar.png' })
81 74
82 await createVideoPlaylist({ 75 await server.playlists.create({
83 url: server.url, 76 attributes: {
84 token: server.accessToken,
85 playlistAttrs: {
86 displayName: 'playlist', 77 displayName: 'playlist',
87 privacy: VideoPlaylistPrivacy.PUBLIC, 78 privacy: VideoPlaylistPrivacy.PUBLIC,
88 videoChannelId: server.videoChannel.id, 79 videoChannelId: server.store.channel.id,
89 thumbnailfile: 'thumbnail.jpg' 80 thumbnailfile: 'thumbnail.jpg'
90 } 81 }
91 }) 82 })
@@ -95,29 +86,27 @@ describe('Test prune storage scripts', function () {
95 86
96 // Lazy load the remote avatar 87 // Lazy load the remote avatar
97 { 88 {
98 const res = await getAccount(servers[0].url, 'root@localhost:' + servers[1].port) 89 const account = await servers[0].accounts.get({ accountName: 'root@localhost:' + servers[1].port })
99 const account: Account = res.body
100 await makeGetRequest({ 90 await makeGetRequest({
101 url: servers[0].url, 91 url: servers[0].url,
102 path: account.avatar.path, 92 path: account.avatar.path,
103 statusCodeExpected: HttpStatusCode.OK_200 93 expectedStatus: HttpStatusCode.OK_200
104 }) 94 })
105 } 95 }
106 96
107 { 97 {
108 const res = await getAccount(servers[1].url, 'root@localhost:' + servers[0].port) 98 const account = await servers[1].accounts.get({ accountName: 'root@localhost:' + servers[0].port })
109 const account: Account = res.body
110 await makeGetRequest({ 99 await makeGetRequest({
111 url: servers[1].url, 100 url: servers[1].url,
112 path: account.avatar.path, 101 path: account.avatar.path,
113 statusCodeExpected: HttpStatusCode.OK_200 102 expectedStatus: HttpStatusCode.OK_200
114 }) 103 })
115 } 104 }
116 105
117 await wait(1000) 106 await wait(1000)
118 107
119 await waitJobs(servers) 108 await waitJobs(servers)
120 killallServers(servers) 109 await killallServers(servers)
121 110
122 await wait(1000) 111 await wait(1000)
123 }) 112 })
@@ -129,7 +118,7 @@ describe('Test prune storage scripts', function () {
129 it('Should create some dirty files', async function () { 118 it('Should create some dirty files', async function () {
130 for (let i = 0; i < 2; i++) { 119 for (let i = 0; i < 2; i++) {
131 { 120 {
132 const base = buildServerDirectory(servers[0], 'videos') 121 const base = servers[0].servers.buildDirectory('videos')
133 122
134 const n1 = buildUUID() + '.mp4' 123 const n1 = buildUUID() + '.mp4'
135 const n2 = buildUUID() + '.webm' 124 const n2 = buildUUID() + '.webm'
@@ -141,7 +130,7 @@ describe('Test prune storage scripts', function () {
141 } 130 }
142 131
143 { 132 {
144 const base = buildServerDirectory(servers[0], 'torrents') 133 const base = servers[0].servers.buildDirectory('torrents')
145 134
146 const n1 = buildUUID() + '-240.torrent' 135 const n1 = buildUUID() + '-240.torrent'
147 const n2 = buildUUID() + '-480.torrent' 136 const n2 = buildUUID() + '-480.torrent'
@@ -153,7 +142,7 @@ describe('Test prune storage scripts', function () {
153 } 142 }
154 143
155 { 144 {
156 const base = buildServerDirectory(servers[0], 'thumbnails') 145 const base = servers[0].servers.buildDirectory('thumbnails')
157 146
158 const n1 = buildUUID() + '.jpg' 147 const n1 = buildUUID() + '.jpg'
159 const n2 = buildUUID() + '.jpg' 148 const n2 = buildUUID() + '.jpg'
@@ -165,7 +154,7 @@ describe('Test prune storage scripts', function () {
165 } 154 }
166 155
167 { 156 {
168 const base = buildServerDirectory(servers[0], 'previews') 157 const base = servers[0].servers.buildDirectory('previews')
169 158
170 const n1 = buildUUID() + '.jpg' 159 const n1 = buildUUID() + '.jpg'
171 const n2 = buildUUID() + '.jpg' 160 const n2 = buildUUID() + '.jpg'
@@ -177,7 +166,7 @@ describe('Test prune storage scripts', function () {
177 } 166 }
178 167
179 { 168 {
180 const base = buildServerDirectory(servers[0], 'avatars') 169 const base = servers[0].servers.buildDirectory('avatars')
181 170
182 const n1 = buildUUID() + '.png' 171 const n1 = buildUUID() + '.png'
183 const n2 = buildUUID() + '.jpg' 172 const n2 = buildUUID() + '.jpg'
@@ -193,8 +182,8 @@ describe('Test prune storage scripts', function () {
193 it('Should run prune storage', async function () { 182 it('Should run prune storage', async function () {
194 this.timeout(30000) 183 this.timeout(30000)
195 184
196 const env = getEnvCli(servers[0]) 185 const env = servers[0].cli.getEnv()
197 await execCLI(`echo y | ${env} npm run prune-storage`) 186 await CLICommand.exec(`echo y | ${env} npm run prune-storage`)
198 }) 187 })
199 188
200 it('Should have removed files', async function () { 189 it('Should have removed files', async function () {
@@ -202,7 +191,7 @@ describe('Test prune storage scripts', function () {
202 191
203 for (const directory of Object.keys(badNames)) { 192 for (const directory of Object.keys(badNames)) {
204 for (const name of badNames[directory]) { 193 for (const name of badNames[directory]) {
205 await assertNotExists(servers[0].internalServerNumber, directory, name) 194 await assertNotExists(servers[0], directory, name)
206 } 195 }
207 } 196 }
208 }) 197 })
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..fcbcb55ba 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,13 +103,11 @@ 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
130 111
131 expect(videoDetails.files).to.have.lengthOf(4) 112 expect(videoDetails.files).to.have.lengthOf(4)
132 113
diff --git a/server/tests/client.ts b/server/tests/client.ts
index 7c4fb4e46..9c27a7aae 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.uuid}" ` +
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,7 +134,7 @@ 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
@@ -163,37 +152,37 @@ 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.uuid}" />`)
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}" />`)
@@ -203,15 +192,15 @@ describe('Test a client controllers', function () {
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
@@ -460,10 +450,10 @@ describe('Test a client controllers', function () {
460 describe('Embed HTML', function () { 450 describe('Embed HTML', function () {
461 451
462 it('Should have the correct embed html tags', async function () { 452 it('Should have the correct embed html tags', async function () {
463 const resConfig = await getConfig(servers[0].url) 453 const config = await servers[0].config.getConfig()
464 const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath) 454 const res = await makeHTMLRequest(servers[0].url, servers[0].store.video.embedPath)
465 455
466 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) 456 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
467 }) 457 })
468 }) 458 })
469 459
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..5667207c0 100644
--- a/server/tests/feeds/feeds.ts
+++ b/server/tests/feeds/feeds.ts
@@ -4,34 +4,15 @@ 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 PeerTubeServer,
19 flushAndRunServer,
20 getJSONfeed,
21 getMyUserInformation,
22 getUserScopedTokens,
23 getXMLfeed,
24 renewUserScopedTokens,
25 ServerInfo,
26 setAccessTokensToServers, 12 setAccessTokensToServers,
27 uploadVideo, 13 waitJobs
28 uploadVideoAndGetId, 14} from '@shared/extra-utils'
29 userLogin 15import { 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 16
36chai.use(require('chai-xml')) 17chai.use(require('chai-xml'))
37chai.use(require('chai-json-schema')) 18chai.use(require('chai-json-schema'))
@@ -39,8 +20,8 @@ chai.config.includeStack = true
39const expect = chai.expect 20const expect = chai.expect
40 21
41describe('Test syndication feeds', () => { 22describe('Test syndication feeds', () => {
42 let servers: ServerInfo[] = [] 23 let servers: PeerTubeServer[] = []
43 let serverHLSOnly: ServerInfo 24 let serverHLSOnly: PeerTubeServer
44 let userAccessToken: string 25 let userAccessToken: string
45 let rootAccountId: number 26 let rootAccountId: number
46 let rootChannelId: number 27 let rootChannelId: number
@@ -52,8 +33,8 @@ describe('Test syndication feeds', () => {
52 this.timeout(120000) 33 this.timeout(120000)
53 34
54 // Run servers 35 // Run servers
55 servers = await flushAndRunMultipleServers(2) 36 servers = await createMultipleServers(2)
56 serverHLSOnly = await flushAndRunServer(3, { 37 serverHLSOnly = await createSingleServer(3, {
57 transcoding: { 38 transcoding: {
58 enabled: true, 39 enabled: true,
59 webtorrent: { enabled: false }, 40 webtorrent: { enabled: false },
@@ -65,50 +46,45 @@ describe('Test syndication feeds', () => {
65 await doubleFollow(servers[0], servers[1]) 46 await doubleFollow(servers[0], servers[1])
66 47
67 { 48 {
68 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 49 const user = await servers[0].users.getMyInfo()
69 const user: User = res.body
70 rootAccountId = user.account.id 50 rootAccountId = user.account.id
71 rootChannelId = user.videoChannels[0].id 51 rootChannelId = user.videoChannels[0].id
72 } 52 }
73 53
74 { 54 {
75 const attr = { username: 'john', password: 'password' } 55 const attr = { username: 'john', password: 'password' }
76 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password }) 56 await servers[0].users.create({ username: attr.username, password: attr.password })
77 userAccessToken = await userLogin(servers[0], attr) 57 userAccessToken = await servers[0].login.getAccessToken(attr)
78 58
79 const res = await getMyUserInformation(servers[0].url, userAccessToken) 59 const user = await servers[0].users.getMyInfo({ token: userAccessToken })
80 const user: User = res.body
81 userAccountId = user.account.id 60 userAccountId = user.account.id
82 userChannelId = user.videoChannels[0].id 61 userChannelId = user.videoChannels[0].id
83 62
84 const res2 = await getUserScopedTokens(servers[0].url, userAccessToken) 63 const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken })
85 const token: ScopedToken = res2.body
86 userFeedToken = token.feedToken 64 userFeedToken = token.feedToken
87 } 65 }
88 66
89 { 67 {
90 await uploadVideo(servers[0].url, userAccessToken, { name: 'user video' }) 68 await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'user video' } })
91 } 69 }
92 70
93 { 71 {
94 const videoAttributes = { 72 const attributes = {
95 name: 'my super name for server 1', 73 name: 'my super name for server 1',
96 description: 'my super description for server 1', 74 description: 'my super description for server 1',
97 fixture: 'video_short.webm' 75 fixture: 'video_short.webm'
98 } 76 }
99 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 77 const { id } = await servers[0].videos.upload({ attributes })
100 const videoId = res.body.video.id
101 78
102 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'super comment 1') 79 await servers[0].comments.createThread({ videoId: id, text: 'super comment 1' })
103 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'super comment 2') 80 await servers[0].comments.createThread({ videoId: id, text: 'super comment 2' })
104 } 81 }
105 82
106 { 83 {
107 const videoAttributes = { name: 'unlisted video', privacy: VideoPrivacy.UNLISTED } 84 const attributes = { name: 'unlisted video', privacy: VideoPrivacy.UNLISTED }
108 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 85 const { id } = await servers[0].videos.upload({ attributes })
109 const videoId = res.body.video.id
110 86
111 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'comment on unlisted video') 87 await servers[0].comments.createThread({ videoId: id, text: 'comment on unlisted video' })
112 } 88 }
113 89
114 await waitJobs(servers) 90 await waitJobs(servers)
@@ -118,18 +94,18 @@ describe('Test syndication feeds', () => {
118 94
119 it('Should be well formed XML (covers RSS 2.0 and ATOM 1.0 endpoints)', async function () { 95 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' ]) { 96 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) {
121 const rss = await getXMLfeed(servers[0].url, feed) 97 const rss = await servers[0].feed.getXML({ feed })
122 expect(rss.text).xml.to.be.valid() 98 expect(rss).xml.to.be.valid()
123 99
124 const atom = await getXMLfeed(servers[0].url, feed, 'atom') 100 const atom = await servers[0].feed.getXML({ feed, format: 'atom' })
125 expect(atom.text).xml.to.be.valid() 101 expect(atom).xml.to.be.valid()
126 } 102 }
127 }) 103 })
128 104
129 it('Should be well formed JSON (covers JSON feed 1.0 endpoint)', async function () { 105 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' ]) { 106 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) {
131 const json = await getJSONfeed(servers[0].url, feed) 107 const jsonText = await servers[0].feed.getJSON({ feed })
132 expect(JSON.parse(json.text)).to.be.jsonSchema({ type: 'object' }) 108 expect(JSON.parse(jsonText)).to.be.jsonSchema({ type: 'object' })
133 } 109 }
134 }) 110 })
135 }) 111 })
@@ -138,10 +114,10 @@ describe('Test syndication feeds', () => {
138 114
139 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () { 115 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () {
140 for (const server of servers) { 116 for (const server of servers) {
141 const rss = await getXMLfeed(server.url, 'videos') 117 const rss = await server.feed.getXML({ feed: 'videos' })
142 expect(xmlParser.validate(rss.text)).to.be.true 118 expect(xmlParser.validate(rss)).to.be.true
143 119
144 const xmlDoc = xmlParser.parse(rss.text, { parseAttributeValue: true, ignoreAttributes: false }) 120 const xmlDoc = xmlParser.parse(rss, { parseAttributeValue: true, ignoreAttributes: false })
145 121
146 const enclosure = xmlDoc.rss.channel.item[0].enclosure 122 const enclosure = xmlDoc.rss.channel.item[0].enclosure
147 expect(enclosure).to.exist 123 expect(enclosure).to.exist
@@ -153,8 +129,8 @@ describe('Test syndication feeds', () => {
153 129
154 it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () { 130 it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () {
155 for (const server of servers) { 131 for (const server of servers) {
156 const json = await getJSONfeed(server.url, 'videos') 132 const json = await server.feed.getJSON({ feed: 'videos' })
157 const jsonObj = JSON.parse(json.text) 133 const jsonObj = JSON.parse(json)
158 expect(jsonObj.items.length).to.be.equal(2) 134 expect(jsonObj.items.length).to.be.equal(2)
159 expect(jsonObj.items[0].attachments).to.exist 135 expect(jsonObj.items[0].attachments).to.exist
160 expect(jsonObj.items[0].attachments.length).to.be.eq(1) 136 expect(jsonObj.items[0].attachments.length).to.be.eq(1)
@@ -166,16 +142,16 @@ describe('Test syndication feeds', () => {
166 142
167 it('Should filter by account', async function () { 143 it('Should filter by account', async function () {
168 { 144 {
169 const json = await getJSONfeed(servers[0].url, 'videos', { accountId: rootAccountId }) 145 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: rootAccountId } })
170 const jsonObj = JSON.parse(json.text) 146 const jsonObj = JSON.parse(json)
171 expect(jsonObj.items.length).to.be.equal(1) 147 expect(jsonObj.items.length).to.be.equal(1)
172 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 148 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
173 expect(jsonObj.items[0].author.name).to.equal('root') 149 expect(jsonObj.items[0].author.name).to.equal('root')
174 } 150 }
175 151
176 { 152 {
177 const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId }) 153 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: userAccountId } })
178 const jsonObj = JSON.parse(json.text) 154 const jsonObj = JSON.parse(json)
179 expect(jsonObj.items.length).to.be.equal(1) 155 expect(jsonObj.items.length).to.be.equal(1)
180 expect(jsonObj.items[0].title).to.equal('user video') 156 expect(jsonObj.items[0].title).to.equal('user video')
181 expect(jsonObj.items[0].author.name).to.equal('john') 157 expect(jsonObj.items[0].author.name).to.equal('john')
@@ -183,15 +159,15 @@ describe('Test syndication feeds', () => {
183 159
184 for (const server of servers) { 160 for (const server of servers) {
185 { 161 {
186 const json = await getJSONfeed(server.url, 'videos', { accountName: 'root@localhost:' + servers[0].port }) 162 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'root@localhost:' + servers[0].port } })
187 const jsonObj = JSON.parse(json.text) 163 const jsonObj = JSON.parse(json)
188 expect(jsonObj.items.length).to.be.equal(1) 164 expect(jsonObj.items.length).to.be.equal(1)
189 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 165 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
190 } 166 }
191 167
192 { 168 {
193 const json = await getJSONfeed(server.url, 'videos', { accountName: 'john@localhost:' + servers[0].port }) 169 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'john@localhost:' + servers[0].port } })
194 const jsonObj = JSON.parse(json.text) 170 const jsonObj = JSON.parse(json)
195 expect(jsonObj.items.length).to.be.equal(1) 171 expect(jsonObj.items.length).to.be.equal(1)
196 expect(jsonObj.items[0].title).to.equal('user video') 172 expect(jsonObj.items[0].title).to.equal('user video')
197 } 173 }
@@ -200,16 +176,16 @@ describe('Test syndication feeds', () => {
200 176
201 it('Should filter by video channel', async function () { 177 it('Should filter by video channel', async function () {
202 { 178 {
203 const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: rootChannelId }) 179 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: rootChannelId } })
204 const jsonObj = JSON.parse(json.text) 180 const jsonObj = JSON.parse(json)
205 expect(jsonObj.items.length).to.be.equal(1) 181 expect(jsonObj.items.length).to.be.equal(1)
206 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')
207 expect(jsonObj.items[0].author.name).to.equal('root') 183 expect(jsonObj.items[0].author.name).to.equal('root')
208 } 184 }
209 185
210 { 186 {
211 const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: userChannelId }) 187 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: userChannelId } })
212 const jsonObj = JSON.parse(json.text) 188 const jsonObj = JSON.parse(json)
213 expect(jsonObj.items.length).to.be.equal(1) 189 expect(jsonObj.items.length).to.be.equal(1)
214 expect(jsonObj.items[0].title).to.equal('user video') 190 expect(jsonObj.items[0].title).to.equal('user video')
215 expect(jsonObj.items[0].author.name).to.equal('john') 191 expect(jsonObj.items[0].author.name).to.equal('john')
@@ -217,15 +193,17 @@ describe('Test syndication feeds', () => {
217 193
218 for (const server of servers) { 194 for (const server of servers) {
219 { 195 {
220 const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'root_channel@localhost:' + servers[0].port }) 196 const query = { videoChannelName: 'root_channel@localhost:' + servers[0].port }
221 const jsonObj = JSON.parse(json.text) 197 const json = await server.feed.getJSON({ feed: 'videos', query })
198 const jsonObj = JSON.parse(json)
222 expect(jsonObj.items.length).to.be.equal(1) 199 expect(jsonObj.items.length).to.be.equal(1)
223 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 200 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
224 } 201 }
225 202
226 { 203 {
227 const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'john_channel@localhost:' + servers[0].port }) 204 const query = { videoChannelName: 'john_channel@localhost:' + servers[0].port }
228 const jsonObj = JSON.parse(json.text) 205 const json = await server.feed.getJSON({ feed: 'videos', query })
206 const jsonObj = JSON.parse(json)
229 expect(jsonObj.items.length).to.be.equal(1) 207 expect(jsonObj.items.length).to.be.equal(1)
230 expect(jsonObj.items[0].title).to.equal('user video') 208 expect(jsonObj.items[0].title).to.equal('user video')
231 } 209 }
@@ -235,12 +213,12 @@ describe('Test syndication feeds', () => {
235 it('Should correctly have videos feed with HLS only', async function () { 213 it('Should correctly have videos feed with HLS only', async function () {
236 this.timeout(120000) 214 this.timeout(120000)
237 215
238 await uploadVideo(serverHLSOnly.url, serverHLSOnly.accessToken, { name: 'hls only video' }) 216 await serverHLSOnly.videos.upload({ attributes: { name: 'hls only video' } })
239 217
240 await waitJobs([ serverHLSOnly ]) 218 await waitJobs([ serverHLSOnly ])
241 219
242 const json = await getJSONfeed(serverHLSOnly.url, 'videos') 220 const json = await serverHLSOnly.feed.getJSON({ feed: 'videos' })
243 const jsonObj = JSON.parse(json.text) 221 const jsonObj = JSON.parse(json)
244 expect(jsonObj.items.length).to.be.equal(1) 222 expect(jsonObj.items.length).to.be.equal(1)
245 expect(jsonObj.items[0].attachments).to.exist 223 expect(jsonObj.items[0].attachments).to.exist
246 expect(jsonObj.items[0].attachments.length).to.be.eq(4) 224 expect(jsonObj.items[0].attachments.length).to.be.eq(4)
@@ -257,9 +235,9 @@ describe('Test syndication feeds', () => {
257 235
258 it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () { 236 it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () {
259 for (const server of servers) { 237 for (const server of servers) {
260 const json = await getJSONfeed(server.url, 'video-comments') 238 const json = await server.feed.getJSON({ feed: 'video-comments' })
261 239
262 const jsonObj = JSON.parse(json.text) 240 const jsonObj = JSON.parse(json)
263 expect(jsonObj.items.length).to.be.equal(2) 241 expect(jsonObj.items.length).to.be.equal(2)
264 expect(jsonObj.items[0].html_content).to.equal('super comment 2') 242 expect(jsonObj.items[0].html_content).to.equal('super comment 2')
265 expect(jsonObj.items[1].html_content).to.equal('super comment 1') 243 expect(jsonObj.items[1].html_content).to.equal('super comment 1')
@@ -271,32 +249,32 @@ describe('Test syndication feeds', () => {
271 249
272 const remoteHandle = 'root@localhost:' + servers[0].port 250 const remoteHandle = 'root@localhost:' + servers[0].port
273 251
274 await addAccountToServerBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) 252 await servers[1].blocklist.addToServerBlocklist({ account: remoteHandle })
275 253
276 { 254 {
277 const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 2 }) 255 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 2 } })
278 const jsonObj = JSON.parse(json.text) 256 const jsonObj = JSON.parse(json)
279 expect(jsonObj.items.length).to.be.equal(0) 257 expect(jsonObj.items.length).to.be.equal(0)
280 } 258 }
281 259
282 await removeAccountFromServerBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) 260 await servers[1].blocklist.removeFromServerBlocklist({ account: remoteHandle })
283 261
284 { 262 {
285 const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'server 2' })).uuid 263 const videoUUID = (await servers[1].videos.quickUpload({ name: 'server 2' })).uuid
286 await waitJobs(servers) 264 await waitJobs(servers)
287 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'super comment') 265 await servers[0].comments.createThread({ videoId: videoUUID, text: 'super comment' })
288 await waitJobs(servers) 266 await waitJobs(servers)
289 267
290 const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 3 }) 268 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 3 } })
291 const jsonObj = JSON.parse(json.text) 269 const jsonObj = JSON.parse(json)
292 expect(jsonObj.items.length).to.be.equal(3) 270 expect(jsonObj.items.length).to.be.equal(3)
293 } 271 }
294 272
295 await addAccountToAccountBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) 273 await servers[1].blocklist.addToMyBlocklist({ account: remoteHandle })
296 274
297 { 275 {
298 const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 4 }) 276 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 4 } })
299 const jsonObj = JSON.parse(json.text) 277 const jsonObj = JSON.parse(json)
300 expect(jsonObj.items.length).to.be.equal(2) 278 expect(jsonObj.items.length).to.be.equal(2)
301 } 279 }
302 }) 280 })
@@ -308,66 +286,64 @@ describe('Test syndication feeds', () => {
308 286
309 it('Should list no videos for a user with no videos and no subscriptions', async function () { 287 it('Should list no videos for a user with no videos and no subscriptions', async function () {
310 const attr = { username: 'feeduser', password: 'password' } 288 const attr = { username: 'feeduser', password: 'password' }
311 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password }) 289 await servers[0].users.create({ username: attr.username, password: attr.password })
312 const feeduserAccessToken = await userLogin(servers[0], attr) 290 const feeduserAccessToken = await servers[0].login.getAccessToken(attr)
313 291
314 { 292 {
315 const res = await getMyUserInformation(servers[0].url, feeduserAccessToken) 293 const user = await servers[0].users.getMyInfo({ token: feeduserAccessToken })
316 const user: User = res.body
317 feeduserAccountId = user.account.id 294 feeduserAccountId = user.account.id
318 } 295 }
319 296
320 { 297 {
321 const res = await getUserScopedTokens(servers[0].url, feeduserAccessToken) 298 const token = await servers[0].users.getMyScopedTokens({ token: feeduserAccessToken })
322 const token: ScopedToken = res.body
323 feeduserFeedToken = token.feedToken 299 feeduserFeedToken = token.feedToken
324 } 300 }
325 301
326 { 302 {
327 const res = await listUserSubscriptionVideos(servers[0].url, feeduserAccessToken) 303 const body = await servers[0].subscriptions.listVideos({ token: feeduserAccessToken })
328 expect(res.body.total).to.equal(0) 304 expect(body.total).to.equal(0)
329 305
330 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: feeduserFeedToken }) 306 const query = { accountId: feeduserAccountId, token: feeduserFeedToken }
331 const jsonObj = JSON.parse(json.text) 307 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
308 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 309 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
333 } 310 }
334 }) 311 })
335 312
336 it('Should fail with an invalid token', async function () { 313 it('Should fail with an invalid token', async function () {
337 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: 'toto' }, HttpStatusCode.FORBIDDEN_403) 314 const query = { accountId: feeduserAccountId, token: 'toto' }
315 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
338 }) 316 })
339 317
340 it('Should fail with a token of another user', async function () { 318 it('Should fail with a token of another user', async function () {
341 await getJSONfeed( 319 const query = { accountId: feeduserAccountId, token: userFeedToken }
342 servers[0].url, 320 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 }) 321 })
348 322
349 it('Should list no videos for a user with videos but no subscriptions', async function () { 323 it('Should list no videos for a user with videos but no subscriptions', async function () {
350 const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) 324 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken })
351 expect(res.body.total).to.equal(0) 325 expect(body.total).to.equal(0)
352 326
353 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken }) 327 const query = { accountId: userAccountId, token: userFeedToken }
354 const jsonObj = JSON.parse(json.text) 328 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
329 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 330 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
356 }) 331 })
357 332
358 it('Should list self videos for a user with a subscription to themselves', async function () { 333 it('Should list self videos for a user with a subscription to themselves', async function () {
359 this.timeout(30000) 334 this.timeout(30000)
360 335
361 await addUserSubscription(servers[0].url, userAccessToken, 'john_channel@localhost:' + servers[0].port) 336 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'john_channel@localhost:' + servers[0].port })
362 await waitJobs(servers) 337 await waitJobs(servers)
363 338
364 { 339 {
365 const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) 340 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken })
366 expect(res.body.total).to.equal(1) 341 expect(body.total).to.equal(1)
367 expect(res.body.data[0].name).to.equal('user video') 342 expect(body.data[0].name).to.equal('user video')
368 343
369 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 1 }) 344 const query = { accountId: userAccountId, token: userFeedToken, version: 1 }
370 const jsonObj = JSON.parse(json.text) 345 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
346 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 347 expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's
372 } 348 }
373 }) 349 })
@@ -375,36 +351,33 @@ describe('Test syndication feeds', () => {
375 it('Should list videos of a user\'s subscription', async function () { 351 it('Should list videos of a user\'s subscription', async function () {
376 this.timeout(30000) 352 this.timeout(30000)
377 353
378 await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[0].port) 354 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@localhost:' + servers[0].port })
379 await waitJobs(servers) 355 await waitJobs(servers)
380 356
381 { 357 {
382 const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) 358 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") 359 expect(body.total).to.equal(2, "there should be 2 videos part of the subscription")
384 360
385 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 2 }) 361 const query = { accountId: userAccountId, token: userFeedToken, version: 2 }
386 const jsonObj = JSON.parse(json.text) 362 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
363 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 364 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 } 365 }
389 }) 366 })
390 367
391 it('Should renew the token, and so have an invalid old token', async function () { 368 it('Should renew the token, and so have an invalid old token', async function () {
392 await renewUserScopedTokens(servers[0].url, userAccessToken) 369 await servers[0].users.renewMyScopedTokens({ token: userAccessToken })
393 370
394 await getJSONfeed( 371 const query = { accountId: userAccountId, token: userFeedToken, version: 3 }
395 servers[0].url, 372 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 }) 373 })
401 374
402 it('Should succeed with the new token', async function () { 375 it('Should succeed with the new token', async function () {
403 const res2 = await getUserScopedTokens(servers[0].url, userAccessToken) 376 const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken })
404 const token: ScopedToken = res2.body
405 userFeedToken = token.feedToken 377 userFeedToken = token.feedToken
406 378
407 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 4 }) 379 const query = { accountId: userAccountId, token: userFeedToken, version: 4 }
380 await servers[0].feed.getJSON({ feed: 'subscriptions', query })
408 }) 381 })
409 382
410 }) 383 })
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/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..c00ac8f91 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,63 @@ 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 describe('Should run filter:video.auto-blacklist.result', function () {
294 259
295 async function checkIsBlacklisted (oldRes: any, value: boolean) { 260 async function checkIsBlacklisted (id: number | string, value: boolean) {
296 const videoId = oldRes.body.video.uuid 261 const video = await servers[0].videos.getWithToken({ id })
297
298 const res = await getVideoWithToken(servers[0].url, servers[0].accessToken, videoId)
299 const video: VideoDetails = res.body
300 expect(video.blacklisted).to.equal(value) 262 expect(video.blacklisted).to.equal(value)
301 } 263 }
302 264
303 it('Should blacklist on upload', async function () { 265 it('Should blacklist on upload', async function () {
304 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video please blacklist me' }) 266 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video please blacklist me' } })
305 await checkIsBlacklisted(res, true) 267 await checkIsBlacklisted(uuid, true)
306 }) 268 })
307 269
308 it('Should blacklist on import', async function () { 270 it('Should blacklist on import', async function () {
@@ -310,60 +272,62 @@ describe('Test plugin filter hooks', function () {
310 272
311 const attributes = { 273 const attributes = {
312 name: 'video please blacklist me', 274 name: 'video please blacklist me',
313 targetUrl: getGoodVideoUrl(), 275 targetUrl: FIXTURE_URLS.goodVideo,
314 channelId: servers[0].videoChannel.id 276 channelId: servers[0].store.channel.id
315 } 277 }
316 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 278 const body = await servers[0].imports.importVideo({ attributes })
317 await checkIsBlacklisted(res, true) 279 await checkIsBlacklisted(body.video.uuid, true)
318 }) 280 })
319 281
320 it('Should blacklist on update', async function () { 282 it('Should blacklist on update', async function () {
321 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) 283 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video' } })
322 const videoId = res.body.video.uuid 284 await checkIsBlacklisted(uuid, false)
323 await checkIsBlacklisted(res, false)
324 285
325 await updateVideo(servers[0].url, servers[0].accessToken, videoId, { name: 'please blacklist me' }) 286 await servers[0].videos.update({ id: uuid, attributes: { name: 'please blacklist me' } })
326 await checkIsBlacklisted(res, true) 287 await checkIsBlacklisted(uuid, true)
327 }) 288 })
328 289
329 it('Should blacklist on remote upload', async function () { 290 it('Should blacklist on remote upload', async function () {
330 this.timeout(120000) 291 this.timeout(120000)
331 292
332 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'remote please blacklist me' }) 293 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'remote please blacklist me' } })
333 await waitJobs(servers) 294 await waitJobs(servers)
334 295
335 await checkIsBlacklisted(res, true) 296 await checkIsBlacklisted(uuid, true)
336 }) 297 })
337 298
338 it('Should blacklist on remote update', async function () { 299 it('Should blacklist on remote update', async function () {
339 this.timeout(120000) 300 this.timeout(120000)
340 301
341 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video' }) 302 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video' } })
342 await waitJobs(servers) 303 await waitJobs(servers)
343 304
344 const videoId = res.body.video.uuid 305 await checkIsBlacklisted(uuid, false)
345 await checkIsBlacklisted(res, false)
346 306
347 await updateVideo(servers[1].url, servers[1].accessToken, videoId, { name: 'please blacklist me' }) 307 await servers[1].videos.update({ id: uuid, attributes: { name: 'please blacklist me' } })
348 await waitJobs(servers) 308 await waitJobs(servers)
349 309
350 await checkIsBlacklisted(res, true) 310 await checkIsBlacklisted(uuid, true)
351 }) 311 })
352 }) 312 })
353 313
354 describe('Should run filter:api.user.signup.allowed.result', function () { 314 describe('Should run filter:api.user.signup.allowed.result', function () {
355 315
356 it('Should run on config endpoint', async function () { 316 it('Should run on config endpoint', async function () {
357 const res = await getConfig(servers[0].url) 317 const body = await servers[0].config.getConfig()
358 expect((res.body as ServerConfig).signup.allowed).to.be.true 318 expect(body.signup.allowed).to.be.true
359 }) 319 })
360 320
361 it('Should allow a signup', async function () { 321 it('Should allow a signup', async function () {
362 await registerUser(servers[0].url, 'john', 'password') 322 await servers[0].users.register({ username: 'john', password: 'password' })
363 }) 323 })
364 324
365 it('Should not allow a signup', async function () { 325 it('Should not allow a signup', async function () {
366 const res = await registerUser(servers[0].url, 'jma', 'password', HttpStatusCode.FORBIDDEN_403) 326 const res = await servers[0].users.register({
327 username: 'jma',
328 password: 'password',
329 expectedStatus: HttpStatusCode.FORBIDDEN_403
330 })
367 331
368 expect(res.body.error).to.equal('No jma') 332 expect(res.body.error).to.equal('No jma')
369 }) 333 })
@@ -375,13 +339,15 @@ describe('Test plugin filter hooks', function () {
375 before(async function () { 339 before(async function () {
376 this.timeout(120000) 340 this.timeout(120000)
377 341
378 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 342 await servers[0].config.updateCustomSubConfig({
379 transcoding: { 343 newConfig: {
380 webtorrent: { 344 transcoding: {
381 enabled: true 345 webtorrent: {
382 }, 346 enabled: true
383 hls: { 347 },
384 enabled: true 348 hls: {
349 enabled: true
350 }
385 } 351 }
386 } 352 }
387 }) 353 })
@@ -389,15 +355,14 @@ describe('Test plugin filter hooks', function () {
389 const uuids: string[] = [] 355 const uuids: string[] = []
390 356
391 for (const name of [ 'bad torrent', 'bad file', 'bad playlist file' ]) { 357 for (const name of [ 'bad torrent', 'bad file', 'bad playlist file' ]) {
392 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid 358 const uuid = (await servers[0].videos.quickUpload({ name: name })).uuid
393 uuids.push(uuid) 359 uuids.push(uuid)
394 } 360 }
395 361
396 await waitJobs(servers) 362 await waitJobs(servers)
397 363
398 for (const uuid of uuids) { 364 for (const uuid of uuids) {
399 const res = await getVideo(servers[0].url, uuid) 365 downloadVideos.push(await servers[0].videos.get({ id: uuid }))
400 downloadVideos.push(res.body)
401 } 366 }
402 }) 367 })
403 368
@@ -437,25 +402,26 @@ describe('Test plugin filter hooks', function () {
437 before(async function () { 402 before(async function () {
438 this.timeout(60000) 403 this.timeout(60000)
439 404
440 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 405 await servers[0].config.updateCustomSubConfig({
441 transcoding: { 406 newConfig: {
442 enabled: false 407 transcoding: {
408 enabled: false
409 }
443 } 410 }
444 }) 411 })
445 412
446 for (const name of [ 'bad embed', 'good embed' ]) { 413 for (const name of [ 'bad embed', 'good embed' ]) {
447 { 414 {
448 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid 415 const uuid = (await servers[0].videos.quickUpload({ name: name })).uuid
449 const res = await getVideo(servers[0].url, uuid) 416 embedVideos.push(await servers[0].videos.get({ id: uuid }))
450 embedVideos.push(res.body)
451 } 417 }
452 418
453 { 419 {
454 const playlistAttrs = { displayName: name, videoChannelId: servers[0].videoChannel.id, privacy: VideoPlaylistPrivacy.PUBLIC } 420 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 }) 421 const { id } = await servers[0].playlists.create({ attributes })
456 422
457 const resPlaylist = await getVideoPlaylist(servers[0].url, res.body.videoPlaylist.id) 423 const playlist = await servers[0].playlists.get({ playlistId: id })
458 embedPlaylists.push(resPlaylist.body) 424 embedPlaylists.push(playlist)
459 } 425 }
460 } 426 }
461 }) 427 })
@@ -474,78 +440,92 @@ describe('Test plugin filter hooks', function () {
474 describe('Search filters', function () { 440 describe('Search filters', function () {
475 441
476 before(async function () { 442 before(async function () {
477 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 443 await servers[0].config.updateCustomSubConfig({
478 search: { 444 newConfig: {
479 searchIndex: { 445 search: {
480 enabled: true, 446 searchIndex: {
481 isDefaultSearch: false, 447 enabled: true,
482 disableLocalSearch: false 448 isDefaultSearch: false,
449 disableLocalSearch: false
450 }
483 } 451 }
484 } 452 }
485 }) 453 })
486 }) 454 })
487 455
488 it('Should run filter:api.search.videos.local.list.{params,result}', async function () { 456 it('Should run filter:api.search.videos.local.list.{params,result}', async function () {
489 await advancedVideosSearch(servers[0].url, { 457 await servers[0].search.advancedVideoSearch({
490 search: 'Sun Quan' 458 search: {
459 search: 'Sun Quan'
460 }
491 }) 461 })
492 462
493 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1) 463 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) 464 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.result', 1)
495 }) 465 })
496 466
497 it('Should run filter:api.search.videos.index.list.{params,result}', async function () { 467 it('Should run filter:api.search.videos.index.list.{params,result}', async function () {
498 await advancedVideosSearch(servers[0].url, { 468 await servers[0].search.advancedVideoSearch({
499 search: 'Sun Quan', 469 search: {
500 searchTarget: 'search-index' 470 search: 'Sun Quan',
471 searchTarget: 'search-index'
472 }
501 }) 473 })
502 474
503 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1) 475 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) 476 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) 477 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) 478 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.index.list.result', 1)
507 }) 479 })
508 480
509 it('Should run filter:api.search.video-channels.local.list.{params,result}', async function () { 481 it('Should run filter:api.search.video-channels.local.list.{params,result}', async function () {
510 await advancedVideoChannelSearch(servers[0].url, { 482 await servers[0].search.advancedChannelSearch({
511 search: 'Sun Ce' 483 search: {
484 search: 'Sun Ce'
485 }
512 }) 486 })
513 487
514 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1) 488 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) 489 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.result', 1)
516 }) 490 })
517 491
518 it('Should run filter:api.search.video-channels.index.list.{params,result}', async function () { 492 it('Should run filter:api.search.video-channels.index.list.{params,result}', async function () {
519 await advancedVideoChannelSearch(servers[0].url, { 493 await servers[0].search.advancedChannelSearch({
520 search: 'Sun Ce', 494 search: {
521 searchTarget: 'search-index' 495 search: 'Sun Ce',
496 searchTarget: 'search-index'
497 }
522 }) 498 })
523 499
524 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1) 500 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) 501 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) 502 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) 503 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.index.list.result', 1)
528 }) 504 })
529 505
530 it('Should run filter:api.search.video-playlists.local.list.{params,result}', async function () { 506 it('Should run filter:api.search.video-playlists.local.list.{params,result}', async function () {
531 await advancedVideoPlaylistSearch(servers[0].url, { 507 await servers[0].search.advancedPlaylistSearch({
532 search: 'Sun Jian' 508 search: {
509 search: 'Sun Jian'
510 }
533 }) 511 })
534 512
535 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.params', 1) 513 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) 514 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.result', 1)
537 }) 515 })
538 516
539 it('Should run filter:api.search.video-playlists.index.list.{params,result}', async function () { 517 it('Should run filter:api.search.video-playlists.index.list.{params,result}', async function () {
540 await advancedVideoPlaylistSearch(servers[0].url, { 518 await servers[0].search.advancedPlaylistSearch({
541 search: 'Sun Jian', 519 search: {
542 searchTarget: 'search-index' 520 search: 'Sun Jian',
521 searchTarget: 'search-index'
522 }
543 }) 523 })
544 524
545 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.params', 1) 525 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) 526 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) 527 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) 528 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.index.list.result', 1)
549 }) 529 })
550 }) 530 })
551 531
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..242994273 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 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(videoUUID, servers[0])
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..0bf1fab01 100644
--- a/server/tests/plugins/plugin-transcoding.ts
+++ b/server/tests/plugins/plugin-transcoding.ts
@@ -4,77 +4,72 @@ import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { join } from 'path' 5import { join } from 'path'
6import { getAudioStream, getVideoFileFPS, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils' 6import { getAudioStream, getVideoFileFPS, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils'
7import { ServerConfig, VideoDetails, VideoPrivacy } from '@shared/models'
8import { 7import {
9 buildServerDirectory, 8 cleanupTests,
10 createLive, 9 createSingleServer,
11 getConfig, 10 PeerTubeServer,
12 getPluginTestPath, 11 PluginsCommand,
13 getVideo,
14 installPlugin,
15 sendRTMPStreamInVideo,
16 setAccessTokensToServers, 12 setAccessTokensToServers,
17 setDefaultVideoChannel, 13 setDefaultVideoChannel,
18 testFfmpegStreamError, 14 testFfmpegStreamError,
19 uninstallPlugin, 15 waitJobs
20 updateCustomSubConfig, 16} from '@shared/extra-utils'
21 uploadVideoAndGetId, 17import { VideoPrivacy } from '@shared/models'
22 waitJobs, 18
23 waitUntilLivePublished 19async 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 = { 20 const liveAttributes = {
29 name: 'live video', 21 name: 'live video',
30 channelId: server.videoChannel.id, 22 channelId: server.store.channel.id,
31 privacy: VideoPrivacy.PUBLIC 23 privacy: VideoPrivacy.PUBLIC
32 } 24 }
33 25
34 const res = await createLive(server.url, server.accessToken, liveAttributes) 26 const { uuid } = await server.live.create({ fields: liveAttributes })
35 return res.body.video.uuid 27
28 return uuid
36} 29}
37 30
38function updateConf (server: ServerInfo, vodProfile: string, liveProfile: string) { 31function updateConf (server: PeerTubeServer, vodProfile: string, liveProfile: string) {
39 return updateCustomSubConfig(server.url, server.accessToken, { 32 return server.config.updateCustomSubConfig({
40 transcoding: { 33 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: { 34 transcoding: {
58 profile: liveProfile,
59 enabled: true, 35 enabled: true,
36 profile: vodProfile,
37 hls: {
38 enabled: true
39 },
40 webtorrent: {
41 enabled: true
42 },
60 resolutions: { 43 resolutions: {
61 '240p': true, 44 '240p': true,
62 '360p': false, 45 '360p': false,
63 '480p': false, 46 '480p': false,
64 '720p': true 47 '720p': true
65 } 48 }
49 },
50 live: {
51 transcoding: {
52 profile: liveProfile,
53 enabled: true,
54 resolutions: {
55 '240p': true,
56 '360p': false,
57 '480p': false,
58 '720p': true
59 }
60 }
66 } 61 }
67 } 62 }
68 }) 63 })
69} 64}
70 65
71describe('Test transcoding plugins', function () { 66describe('Test transcoding plugins', function () {
72 let server: ServerInfo 67 let server: PeerTubeServer
73 68
74 before(async function () { 69 before(async function () {
75 this.timeout(60000) 70 this.timeout(60000)
76 71
77 server = await flushAndRunServer(1) 72 server = await createSingleServer(1)
78 await setAccessTokensToServers([ server ]) 73 await setAccessTokensToServers([ server ])
79 await setDefaultVideoChannel([ server ]) 74 await setDefaultVideoChannel([ server ])
80 75
@@ -84,8 +79,7 @@ describe('Test transcoding plugins', function () {
84 describe('When using a plugin adding profiles to existing encoders', function () { 79 describe('When using a plugin adding profiles to existing encoders', function () {
85 80
86 async function checkVideoFPS (uuid: string, type: 'above' | 'below', fps: number) { 81 async function checkVideoFPS (uuid: string, type: 'above' | 'below', fps: number) {
87 const res = await getVideo(server.url, uuid) 82 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)) 83 const files = video.files.concat(...video.streamingPlaylists.map(p => p.files))
90 84
91 for (const file of files) { 85 for (const file of files) {
@@ -109,16 +103,11 @@ describe('Test transcoding plugins', function () {
109 } 103 }
110 104
111 before(async function () { 105 before(async function () {
112 await installPlugin({ 106 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-transcoding-one') })
113 url: server.url,
114 accessToken: server.accessToken,
115 path: getPluginTestPath('-transcoding-one')
116 })
117 }) 107 })
118 108
119 it('Should have the appropriate available profiles', async function () { 109 it('Should have the appropriate available profiles', async function () {
120 const res = await getConfig(server.url) 110 const config = await server.config.getConfig()
121 const config = res.body as ServerConfig
122 111
123 expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod', 'input-options-vod', 'bad-scale-vod' ]) 112 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' ]) 113 expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'low-live', 'input-options-live', 'bad-scale-live' ])
@@ -127,7 +116,7 @@ describe('Test transcoding plugins', function () {
127 it('Should not use the plugin profile if not chosen by the admin', async function () { 116 it('Should not use the plugin profile if not chosen by the admin', async function () {
128 this.timeout(240000) 117 this.timeout(240000)
129 118
130 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 119 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
131 await waitJobs([ server ]) 120 await waitJobs([ server ])
132 121
133 await checkVideoFPS(videoUUID, 'above', 20) 122 await checkVideoFPS(videoUUID, 'above', 20)
@@ -138,7 +127,7 @@ describe('Test transcoding plugins', function () {
138 127
139 await updateConf(server, 'low-vod', 'default') 128 await updateConf(server, 'low-vod', 'default')
140 129
141 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 130 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
142 await waitJobs([ server ]) 131 await waitJobs([ server ])
143 132
144 await checkVideoFPS(videoUUID, 'below', 12) 133 await checkVideoFPS(videoUUID, 'below', 12)
@@ -149,7 +138,7 @@ describe('Test transcoding plugins', function () {
149 138
150 await updateConf(server, 'input-options-vod', 'default') 139 await updateConf(server, 'input-options-vod', 'default')
151 140
152 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 141 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
153 await waitJobs([ server ]) 142 await waitJobs([ server ])
154 143
155 await checkVideoFPS(videoUUID, 'below', 6) 144 await checkVideoFPS(videoUUID, 'below', 6)
@@ -160,13 +149,11 @@ describe('Test transcoding plugins', function () {
160 149
161 await updateConf(server, 'bad-scale-vod', 'default') 150 await updateConf(server, 'bad-scale-vod', 'default')
162 151
163 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 152 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
164 await waitJobs([ server ]) 153 await waitJobs([ server ])
165 154
166 // Transcoding failed 155 // Transcoding failed
167 const res = await getVideo(server.url, videoUUID) 156 const video = await server.videos.get({ id: videoUUID })
168 const video: VideoDetails = res.body
169
170 expect(video.files).to.have.lengthOf(1) 157 expect(video.files).to.have.lengthOf(1)
171 expect(video.streamingPlaylists).to.have.lengthOf(0) 158 expect(video.streamingPlaylists).to.have.lengthOf(0)
172 }) 159 })
@@ -176,8 +163,8 @@ describe('Test transcoding plugins', function () {
176 163
177 const liveVideoId = await createLiveWrapper(server) 164 const liveVideoId = await createLiveWrapper(server)
178 165
179 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 166 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
180 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 167 await server.live.waitUntilPublished({ videoId: liveVideoId })
181 await waitJobs([ server ]) 168 await waitJobs([ server ])
182 169
183 await checkLiveFPS(liveVideoId, 'above', 20) 170 await checkLiveFPS(liveVideoId, 'above', 20)
@@ -190,8 +177,8 @@ describe('Test transcoding plugins', function () {
190 177
191 const liveVideoId = await createLiveWrapper(server) 178 const liveVideoId = await createLiveWrapper(server)
192 179
193 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 180 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
194 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 181 await server.live.waitUntilPublished({ videoId: liveVideoId })
195 await waitJobs([ server ]) 182 await waitJobs([ server ])
196 183
197 await checkLiveFPS(liveVideoId, 'below', 12) 184 await checkLiveFPS(liveVideoId, 'below', 12)
@@ -204,8 +191,8 @@ describe('Test transcoding plugins', function () {
204 191
205 const liveVideoId = await createLiveWrapper(server) 192 const liveVideoId = await createLiveWrapper(server)
206 193
207 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 194 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
208 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 195 await server.live.waitUntilPublished({ videoId: liveVideoId })
209 await waitJobs([ server ]) 196 await waitJobs([ server ])
210 197
211 await checkLiveFPS(liveVideoId, 'below', 6) 198 await checkLiveFPS(liveVideoId, 'below', 6)
@@ -218,22 +205,21 @@ describe('Test transcoding plugins', function () {
218 205
219 const liveVideoId = await createLiveWrapper(server) 206 const liveVideoId = await createLiveWrapper(server)
220 207
221 const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 208 const command = await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
222 await testFfmpegStreamError(command, true) 209 await testFfmpegStreamError(command, true)
223 }) 210 })
224 211
225 it('Should default to the default profile if the specified profile does not exist', async function () { 212 it('Should default to the default profile if the specified profile does not exist', async function () {
226 this.timeout(240000) 213 this.timeout(240000)
227 214
228 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-test-transcoding-one' }) 215 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-transcoding-one' })
229 216
230 const res = await getConfig(server.url) 217 const config = await server.config.getConfig()
231 const config = res.body as ServerConfig
232 218
233 expect(config.transcoding.availableProfiles).to.deep.equal([ 'default' ]) 219 expect(config.transcoding.availableProfiles).to.deep.equal([ 'default' ])
234 expect(config.live.transcoding.availableProfiles).to.deep.equal([ 'default' ]) 220 expect(config.live.transcoding.availableProfiles).to.deep.equal([ 'default' ])
235 221
236 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 222 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
237 await waitJobs([ server ]) 223 await waitJobs([ server ])
238 224
239 await checkVideoFPS(videoUUID, 'above', 20) 225 await checkVideoFPS(videoUUID, 'above', 20)
@@ -244,11 +230,7 @@ describe('Test transcoding plugins', function () {
244 describe('When using a plugin adding new encoders', function () { 230 describe('When using a plugin adding new encoders', function () {
245 231
246 before(async function () { 232 before(async function () {
247 await installPlugin({ 233 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-transcoding-two') })
248 url: server.url,
249 accessToken: server.accessToken,
250 path: getPluginTestPath('-transcoding-two')
251 })
252 234
253 await updateConf(server, 'test-vod-profile', 'test-live-profile') 235 await updateConf(server, 'test-vod-profile', 'test-live-profile')
254 }) 236 })
@@ -256,10 +238,10 @@ describe('Test transcoding plugins', function () {
256 it('Should use the new vod encoders', async function () { 238 it('Should use the new vod encoders', async function () {
257 this.timeout(240000) 239 this.timeout(240000)
258 240
259 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video', fixture: 'video_short_240p.mp4' })).uuid 241 const videoUUID = (await server.videos.quickUpload({ name: 'video', fixture: 'video_short_240p.mp4' })).uuid
260 await waitJobs([ server ]) 242 await waitJobs([ server ])
261 243
262 const path = buildServerDirectory(server, join('videos', videoUUID + '-240.mp4')) 244 const path = server.servers.buildDirectory(join('videos', videoUUID + '-240.mp4'))
263 const audioProbe = await getAudioStream(path) 245 const audioProbe = await getAudioStream(path)
264 expect(audioProbe.audioStream.codec_name).to.equal('opus') 246 expect(audioProbe.audioStream.codec_name).to.equal('opus')
265 247
@@ -272,8 +254,8 @@ describe('Test transcoding plugins', function () {
272 254
273 const liveVideoId = await createLiveWrapper(server) 255 const liveVideoId = await createLiveWrapper(server)
274 256
275 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 257 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
276 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 258 await server.live.waitUntilPublished({ videoId: liveVideoId })
277 await waitJobs([ server ]) 259 await waitJobs([ server ])
278 260
279 const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8` 261 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 7b1312f88..19cba6c2c 100644
--- a/server/tests/plugins/video-constants.ts
+++ b/server/tests/plugins/video-constants.ts
@@ -1,47 +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,
10 getVideoCategories,
11 getVideoLanguages,
12 getVideoLicences,
13 getVideoPlaylistPrivacies,
14 getVideoPrivacies,
15 installPlugin,
16 makeGetRequest, 8 makeGetRequest,
17 setAccessTokensToServers, 9 PeerTubeServer,
18 uninstallPlugin, 10 PluginsCommand,
19 uploadVideo 11 setAccessTokensToServers
20} from '../../../shared/extra-utils' 12} from '@shared/extra-utils'
21import { VideoDetails, VideoPlaylistPrivacy } from '../../../shared/models/videos' 13import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
22import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
23 14
24const expect = chai.expect 15const expect = chai.expect
25 16
26describe('Test plugin altering video constants', function () { 17describe('Test plugin altering video constants', function () {
27 let server: ServerInfo 18 let server: PeerTubeServer
28 19
29 before(async function () { 20 before(async function () {
30 this.timeout(30000) 21 this.timeout(30000)
31 22
32 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
33 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
34 25
35 await installPlugin({ 26 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-video-constants') })
36 url: server.url,
37 accessToken: server.accessToken,
38 path: getPluginTestPath('-video-constants')
39 })
40 }) 27 })
41 28
42 it('Should have updated languages', async function () { 29 it('Should have updated languages', async function () {
43 const res = await getVideoLanguages(server.url) 30 const languages = await server.videos.getLanguages()
44 const languages = res.body
45 31
46 expect(languages['en']).to.not.exist 32 expect(languages['en']).to.not.exist
47 expect(languages['fr']).to.not.exist 33 expect(languages['fr']).to.not.exist
@@ -52,8 +38,7 @@ describe('Test plugin altering video constants', function () {
52 }) 38 })
53 39
54 it('Should have updated categories', async function () { 40 it('Should have updated categories', async function () {
55 const res = await getVideoCategories(server.url) 41 const categories = await server.videos.getCategories()
56 const categories = res.body
57 42
58 expect(categories[1]).to.not.exist 43 expect(categories[1]).to.not.exist
59 expect(categories[2]).to.not.exist 44 expect(categories[2]).to.not.exist
@@ -63,8 +48,7 @@ describe('Test plugin altering video constants', function () {
63 }) 48 })
64 49
65 it('Should have updated licences', async function () { 50 it('Should have updated licences', async function () {
66 const res = await getVideoLicences(server.url) 51 const licences = await server.videos.getLicences()
67 const licences = res.body
68 52
69 expect(licences[1]).to.not.exist 53 expect(licences[1]).to.not.exist
70 expect(licences[7]).to.not.exist 54 expect(licences[7]).to.not.exist
@@ -74,8 +58,7 @@ describe('Test plugin altering video constants', function () {
74 }) 58 })
75 59
76 it('Should have updated video privacies', async function () { 60 it('Should have updated video privacies', async function () {
77 const res = await getVideoPrivacies(server.url) 61 const privacies = await server.videos.getPrivacies()
78 const privacies = res.body
79 62
80 expect(privacies[1]).to.exist 63 expect(privacies[1]).to.exist
81 expect(privacies[2]).to.not.exist 64 expect(privacies[2]).to.not.exist
@@ -84,8 +67,7 @@ describe('Test plugin altering video constants', function () {
84 }) 67 })
85 68
86 it('Should have updated playlist privacies', async function () { 69 it('Should have updated playlist privacies', async function () {
87 const res = await getVideoPlaylistPrivacies(server.url) 70 const playlistPrivacies = await server.playlists.getPrivacies()
88 const playlistPrivacies = res.body
89 71
90 expect(playlistPrivacies[1]).to.exist 72 expect(playlistPrivacies[1]).to.exist
91 expect(playlistPrivacies[2]).to.exist 73 expect(playlistPrivacies[2]).to.exist
@@ -93,38 +75,30 @@ describe('Test plugin altering video constants', function () {
93 }) 75 })
94 76
95 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 () {
96 const attrs = { name: 'video', privacy: 2 } 78 const attributes = { name: 'video', privacy: 2 }
97 await uploadVideo(server.url, server.accessToken, attrs, HttpStatusCode.BAD_REQUEST_400) 79 await server.videos.upload({ attributes, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
98 }) 80 })
99 81
100 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 () {
101 const attrs = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE } 83 const attributes = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE }
102 await createVideoPlaylist({ 84 await server.playlists.create({ attributes, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
103 url: server.url,
104 token: server.accessToken,
105 playlistAttrs: attrs,
106 expectedStatus: HttpStatusCode.BAD_REQUEST_400
107 })
108 }) 85 })
109 86
110 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 () {
111 const attrs = { name: 'video', category: 42, licence: 42, language: 'al_bhed2' } 88 const attributes = { name: 'video', category: 42, licence: 42, language: 'al_bhed2' }
112 const resUpload = await uploadVideo(server.url, server.accessToken, attrs) 89 const { uuid } = await server.videos.upload({ attributes })
113 90
114 const res = await getVideo(server.url, resUpload.body.video.uuid) 91 const video = await server.videos.get({ id: uuid })
115
116 const video: VideoDetails = res.body
117 expect(video.language.label).to.equal('Al Bhed 2') 92 expect(video.language.label).to.equal('Al Bhed 2')
118 expect(video.licence.label).to.equal('Best licence') 93 expect(video.licence.label).to.equal('Best licence')
119 expect(video.category.label).to.equal('Best category') 94 expect(video.category.label).to.equal('Best category')
120 }) 95 })
121 96
122 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 () {
123 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' })
124 99
125 { 100 {
126 const res = await getVideoLanguages(server.url) 101 const languages = await server.videos.getLanguages()
127 const languages = res.body
128 102
129 expect(languages['en']).to.equal('English') 103 expect(languages['en']).to.equal('English')
130 expect(languages['fr']).to.equal('French') 104 expect(languages['fr']).to.equal('French')
@@ -135,8 +109,7 @@ describe('Test plugin altering video constants', function () {
135 } 109 }
136 110
137 { 111 {
138 const res = await getVideoCategories(server.url) 112 const categories = await server.videos.getCategories()
139 const categories = res.body
140 113
141 expect(categories[1]).to.equal('Music') 114 expect(categories[1]).to.equal('Music')
142 expect(categories[2]).to.equal('Films') 115 expect(categories[2]).to.equal('Films')
@@ -146,8 +119,7 @@ describe('Test plugin altering video constants', function () {
146 } 119 }
147 120
148 { 121 {
149 const res = await getVideoLicences(server.url) 122 const licences = await server.videos.getLicences()
150 const licences = res.body
151 123
152 expect(licences[1]).to.equal('Attribution') 124 expect(licences[1]).to.equal('Attribution')
153 expect(licences[7]).to.equal('Public Domain Dedication') 125 expect(licences[7]).to.equal('Public Domain Dedication')
@@ -157,8 +129,7 @@ describe('Test plugin altering video constants', function () {
157 } 129 }
158 130
159 { 131 {
160 const res = await getVideoPrivacies(server.url) 132 const privacies = await server.videos.getPrivacies()
161 const privacies = res.body
162 133
163 expect(privacies[1]).to.exist 134 expect(privacies[1]).to.exist
164 expect(privacies[2]).to.exist 135 expect(privacies[2]).to.exist
@@ -167,8 +138,7 @@ describe('Test plugin altering video constants', function () {
167 } 138 }
168 139
169 { 140 {
170 const res = await getVideoPlaylistPrivacies(server.url) 141 const playlistPrivacies = await server.playlists.getPrivacies()
171 const playlistPrivacies = res.body
172 142
173 expect(playlistPrivacies[1]).to.exist 143 expect(playlistPrivacies[1]).to.exist
174 expect(playlistPrivacies[2]).to.exist 144 expect(playlistPrivacies[2]).to.exist
@@ -177,35 +147,34 @@ describe('Test plugin altering video constants', function () {
177 }) 147 })
178 148
179 it('Should be able to reset categories', async function () { 149 it('Should be able to reset categories', async function () {
180 await installPlugin({ 150 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-video-constants') })
181 url: server.url,
182 accessToken: server.accessToken,
183 path: getPluginTestPath('-video-constants')
184 })
185 151
186 let { body: categories } = await getVideoCategories(server.url) 152 {
153 const categories = await server.videos.getCategories()
187 154
188 expect(categories[1]).to.not.exist 155 expect(categories[1]).to.not.exist
189 expect(categories[2]).to.not.exist 156 expect(categories[2]).to.not.exist
190 157
191 expect(categories[42]).to.exist 158 expect(categories[42]).to.exist
192 expect(categories[43]).to.exist 159 expect(categories[43]).to.exist
160 }
193 161
194 await makeGetRequest({ 162 await makeGetRequest({
195 url: server.url, 163 url: server.url,
196 token: server.accessToken, 164 token: server.accessToken,
197 path: '/plugins/test-video-constants/router/reset-categories', 165 path: '/plugins/test-video-constants/router/reset-categories',
198 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 166 expectedStatus: HttpStatusCode.NO_CONTENT_204
199 }) 167 })
200 168
201 const { body } = await getVideoCategories(server.url) 169 {
202 categories = body 170 const categories = await server.videos.getCategories()
203 171
204 expect(categories[1]).to.exist 172 expect(categories[1]).to.exist
205 expect(categories[2]).to.exist 173 expect(categories[2]).to.exist
206 174
207 expect(categories[42]).to.not.exist 175 expect(categories[42]).to.not.exist
208 expect(categories[43]).to.not.exist 176 expect(categories[43]).to.not.exist
177 }
209 }) 178 })
210 179
211 after(async function () { 180 after(async function () {
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..0cb0c3668 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, execArgv: [ '--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/typings/express/index.d.ts b/server/typings/express/index.d.ts
index 1a8dc3430..5469f3b83 100644
--- a/server/typings/express/index.d.ts
+++ b/server/typings/express/index.d.ts
@@ -22,8 +22,7 @@ import { MPlugin, MServer, MServerBlocklist } from '@server/types/models/server'
22import { MVideoImportDefault } from '@server/types/models/video/video-import' 22import { MVideoImportDefault } from '@server/types/models/video/video-import'
23import { MVideoPlaylistElement, MVideoPlaylistElementVideoUrlPlaylistPrivacy } from '@server/types/models/video/video-playlist-element' 23import { MVideoPlaylistElement, MVideoPlaylistElementVideoUrlPlaylistPrivacy } from '@server/types/models/video/video-playlist-element'
24import { MAccountVideoRateAccountVideo } from '@server/types/models/video/video-rate' 24import { MAccountVideoRateAccountVideo } from '@server/types/models/video/video-rate'
25import { HttpMethod } from '@shared/core-utils/miscs/http-methods' 25import { HttpMethod, PeerTubeProblemDocumentData, ServerErrorCode, VideoCreate } from '@shared/models'
26import { PeerTubeProblemDocumentData, ServerErrorCode, VideoCreate } from '@shared/models'
27import { File as UploadXFile, Metadata } from '@uploadx/core' 26import { File as UploadXFile, Metadata } from '@uploadx/core'
28import { RegisteredPlugin } from '../../lib/plugins/plugin-manager' 27import { RegisteredPlugin } from '../../lib/plugins/plugin-manager'
29import { 28import {
diff --git a/shared/core-utils/miscs/index.ts b/shared/core-utils/miscs/index.ts
index 251df1de2..afd147f24 100644
--- a/shared/core-utils/miscs/index.ts
+++ b/shared/core-utils/miscs/index.ts
@@ -1,5 +1,3 @@
1export * from './date' 1export * from './date'
2export * from './miscs' 2export * from './miscs'
3export * from './types' 3export * from './types'
4export * from './http-error-codes'
5export * from './http-methods'
diff --git a/shared/extra-utils/bulk/bulk-command.ts b/shared/extra-utils/bulk/bulk-command.ts
new file mode 100644
index 000000000..b5c5673ce
--- /dev/null
+++ b/shared/extra-utils/bulk/bulk-command.ts
@@ -0,0 +1,20 @@
1import { BulkRemoveCommentsOfBody, HttpStatusCode } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class BulkCommand extends AbstractCommand {
5
6 removeCommentsOf (options: OverrideCommandOptions & {
7 attributes: BulkRemoveCommentsOfBody
8 }) {
9 const { attributes } = options
10
11 return this.postBodyRequest({
12 ...options,
13
14 path: '/api/v1/bulk/remove-comments-of',
15 fields: attributes,
16 implicitToken: true,
17 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
18 })
19 }
20}
diff --git a/shared/extra-utils/bulk/bulk.ts b/shared/extra-utils/bulk/bulk.ts
deleted file mode 100644
index b6f437b8b..000000000
--- a/shared/extra-utils/bulk/bulk.ts
+++ /dev/null
@@ -1,25 +0,0 @@
1import { BulkRemoveCommentsOfBody } from "@shared/models/bulk/bulk-remove-comments-of-body.model"
2import { makePostBodyRequest } from "../requests/requests"
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function bulkRemoveCommentsOf (options: {
6 url: string
7 token: string
8 attributes: BulkRemoveCommentsOfBody
9 expectedStatus?: number
10}) {
11 const { url, token, attributes, expectedStatus } = options
12 const path = '/api/v1/bulk/remove-comments-of'
13
14 return makePostBodyRequest({
15 url,
16 path,
17 token,
18 fields: attributes,
19 statusCodeExpected: expectedStatus || HttpStatusCode.NO_CONTENT_204
20 })
21}
22
23export {
24 bulkRemoveCommentsOf
25}
diff --git a/shared/extra-utils/bulk/index.ts b/shared/extra-utils/bulk/index.ts
new file mode 100644
index 000000000..391597243
--- /dev/null
+++ b/shared/extra-utils/bulk/index.ts
@@ -0,0 +1 @@
export * from './bulk-command'
diff --git a/shared/extra-utils/cli/cli-command.ts b/shared/extra-utils/cli/cli-command.ts
new file mode 100644
index 000000000..bc1dddc68
--- /dev/null
+++ b/shared/extra-utils/cli/cli-command.ts
@@ -0,0 +1,23 @@
1import { exec } from 'child_process'
2import { AbstractCommand } from '../shared'
3
4export class CLICommand extends AbstractCommand {
5
6 static exec (command: string) {
7 return new Promise<string>((res, rej) => {
8 exec(command, (err, stdout, _stderr) => {
9 if (err) return rej(err)
10
11 return res(stdout)
12 })
13 })
14 }
15
16 getEnv () {
17 return `NODE_ENV=test NODE_APP_INSTANCE=${this.server.internalServerNumber}`
18 }
19
20 async execWithEnv (command: string) {
21 return CLICommand.exec(`${this.getEnv()} ${command}`)
22 }
23}
diff --git a/shared/extra-utils/cli/cli.ts b/shared/extra-utils/cli/cli.ts
deleted file mode 100644
index c62e170bb..000000000
--- a/shared/extra-utils/cli/cli.ts
+++ /dev/null
@@ -1,24 +0,0 @@
1import { exec } from 'child_process'
2
3import { ServerInfo } from '../server/servers'
4
5function getEnvCli (server?: ServerInfo) {
6 return `NODE_ENV=test NODE_APP_INSTANCE=${server.internalServerNumber}`
7}
8
9async function execCLI (command: string) {
10 return new Promise<string>((res, rej) => {
11 exec(command, (err, stdout, stderr) => {
12 if (err) return rej(err)
13
14 return res(stdout)
15 })
16 })
17}
18
19// ---------------------------------------------------------------------------
20
21export {
22 execCLI,
23 getEnvCli
24}
diff --git a/shared/extra-utils/cli/index.ts b/shared/extra-utils/cli/index.ts
new file mode 100644
index 000000000..91b5abfbe
--- /dev/null
+++ b/shared/extra-utils/cli/index.ts
@@ -0,0 +1 @@
export * from './cli-command'
diff --git a/shared/extra-utils/custom-pages/custom-pages-command.ts b/shared/extra-utils/custom-pages/custom-pages-command.ts
new file mode 100644
index 000000000..cd869a8de
--- /dev/null
+++ b/shared/extra-utils/custom-pages/custom-pages-command.ts
@@ -0,0 +1,33 @@
1import { CustomPage, HttpStatusCode } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class CustomPagesCommand extends AbstractCommand {
5
6 getInstanceHomepage (options: OverrideCommandOptions = {}) {
7 const path = '/api/v1/custom-pages/homepage/instance'
8
9 return this.getRequestBody<CustomPage>({
10 ...options,
11
12 path,
13 implicitToken: false,
14 defaultExpectedStatus: HttpStatusCode.OK_200
15 })
16 }
17
18 updateInstanceHomepage (options: OverrideCommandOptions & {
19 content: string
20 }) {
21 const { content } = options
22 const path = '/api/v1/custom-pages/homepage/instance'
23
24 return this.putBodyRequest({
25 ...options,
26
27 path,
28 fields: { content },
29 implicitToken: true,
30 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
31 })
32 }
33}
diff --git a/shared/extra-utils/custom-pages/custom-pages.ts b/shared/extra-utils/custom-pages/custom-pages.ts
deleted file mode 100644
index bf2d16c70..000000000
--- a/shared/extra-utils/custom-pages/custom-pages.ts
+++ /dev/null
@@ -1,31 +0,0 @@
1import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
2import { makeGetRequest, makePutBodyRequest } from '../requests/requests'
3
4function getInstanceHomepage (url: string, statusCodeExpected = HttpStatusCode.OK_200) {
5 const path = '/api/v1/custom-pages/homepage/instance'
6
7 return makeGetRequest({
8 url,
9 path,
10 statusCodeExpected
11 })
12}
13
14function updateInstanceHomepage (url: string, token: string, content: string) {
15 const path = '/api/v1/custom-pages/homepage/instance'
16
17 return makePutBodyRequest({
18 url,
19 path,
20 token,
21 fields: { content },
22 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
23 })
24}
25
26// ---------------------------------------------------------------------------
27
28export {
29 getInstanceHomepage,
30 updateInstanceHomepage
31}
diff --git a/shared/extra-utils/custom-pages/index.ts b/shared/extra-utils/custom-pages/index.ts
new file mode 100644
index 000000000..58aed04f2
--- /dev/null
+++ b/shared/extra-utils/custom-pages/index.ts
@@ -0,0 +1 @@
export * from './custom-pages-command'
diff --git a/shared/extra-utils/feeds/feeds-command.ts b/shared/extra-utils/feeds/feeds-command.ts
new file mode 100644
index 000000000..3c95f9536
--- /dev/null
+++ b/shared/extra-utils/feeds/feeds-command.ts
@@ -0,0 +1,44 @@
1
2import { HttpStatusCode } from '@shared/models'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5type FeedType = 'videos' | 'video-comments' | 'subscriptions'
6
7export class FeedCommand extends AbstractCommand {
8
9 getXML (options: OverrideCommandOptions & {
10 feed: FeedType
11 format?: string
12 }) {
13 const { feed, format } = options
14 const path = '/feeds/' + feed + '.xml'
15
16 return this.getRequestText({
17 ...options,
18
19 path,
20 query: format ? { format } : undefined,
21 accept: 'application/xml',
22 implicitToken: false,
23 defaultExpectedStatus: HttpStatusCode.OK_200
24 })
25 }
26
27 getJSON (options: OverrideCommandOptions & {
28 feed: FeedType
29 query?: { [ id: string ]: any }
30 }) {
31 const { feed, query } = options
32 const path = '/feeds/' + feed + '.json'
33
34 return this.getRequestText({
35 ...options,
36
37 path,
38 query,
39 accept: 'application/json',
40 implicitToken: false,
41 defaultExpectedStatus: HttpStatusCode.OK_200
42 })
43 }
44}
diff --git a/shared/extra-utils/feeds/feeds.ts b/shared/extra-utils/feeds/feeds.ts
deleted file mode 100644
index ce0a98c6d..000000000
--- a/shared/extra-utils/feeds/feeds.ts
+++ /dev/null
@@ -1,33 +0,0 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4type FeedType = 'videos' | 'video-comments' | 'subscriptions'
5
6function getXMLfeed (url: string, feed: FeedType, format?: string) {
7 const path = '/feeds/' + feed + '.xml'
8
9 return request(url)
10 .get(path)
11 .query((format) ? { format: format } : {})
12 .set('Accept', 'application/xml')
13 .expect(HttpStatusCode.OK_200)
14 .expect('Content-Type', /xml/)
15}
16
17function getJSONfeed (url: string, feed: FeedType, query: any = {}, statusCodeExpected = HttpStatusCode.OK_200) {
18 const path = '/feeds/' + feed + '.json'
19
20 return request(url)
21 .get(path)
22 .query(query)
23 .set('Accept', 'application/json')
24 .expect(statusCodeExpected)
25 .expect('Content-Type', /json/)
26}
27
28// ---------------------------------------------------------------------------
29
30export {
31 getXMLfeed,
32 getJSONfeed
33}
diff --git a/shared/extra-utils/feeds/index.ts b/shared/extra-utils/feeds/index.ts
new file mode 100644
index 000000000..662a22b6f
--- /dev/null
+++ b/shared/extra-utils/feeds/index.ts
@@ -0,0 +1 @@
export * from './feeds-command'
diff --git a/shared/extra-utils/index.ts b/shared/extra-utils/index.ts
index 87ee8abba..4b3636d06 100644
--- a/shared/extra-utils/index.ts
+++ b/shared/extra-utils/index.ts
@@ -1,51 +1,15 @@
1export * from './bulk/bulk' 1export * from './bulk'
2 2export * from './cli'
3export * from './cli/cli' 3export * from './custom-pages'
4 4export * from './feeds'
5export * from './custom-pages/custom-pages' 5export * from './logs'
6 6export * from './miscs'
7export * from './feeds/feeds' 7export * from './mock-servers'
8 8export * from './moderation'
9export * from './mock-servers/mock-instances-index' 9export * from './overviews'
10 10export * from './requests'
11export * from './miscs/email' 11export * from './search'
12export * from './miscs/sql' 12export * from './server'
13export * from './miscs/miscs' 13export * from './socket'
14export * from './miscs/stubs' 14export * from './users'
15 15export * from './videos'
16export * from './moderation/abuses'
17export * from './plugins/mock-blocklist'
18
19export * from './requests/check-api-params'
20export * from './requests/requests'
21
22export * from './search/video-channels'
23export * from './search/video-playlists'
24export * from './search/videos'
25
26export * from './server/activitypub'
27export * from './server/clients'
28export * from './server/config'
29export * from './server/debug'
30export * from './server/follows'
31export * from './server/jobs'
32export * from './server/plugins'
33export * from './server/servers'
34
35export * from './users/accounts'
36export * from './users/blocklist'
37export * from './users/login'
38export * from './users/user-notifications'
39export * from './users/user-subscriptions'
40export * from './users/users'
41
42export * from './videos/live'
43export * from './videos/services'
44export * from './videos/video-blacklist'
45export * from './videos/video-captions'
46export * from './videos/video-change-ownership'
47export * from './videos/video-channels'
48export * from './videos/video-comments'
49export * from './videos/video-playlists'
50export * from './videos/video-streaming-playlists'
51export * from './videos/videos'
diff --git a/shared/extra-utils/logs/index.ts b/shared/extra-utils/logs/index.ts
new file mode 100644
index 000000000..69452d7f0
--- /dev/null
+++ b/shared/extra-utils/logs/index.ts
@@ -0,0 +1 @@
export * from './logs-command'
diff --git a/shared/extra-utils/logs/logs-command.ts b/shared/extra-utils/logs/logs-command.ts
new file mode 100644
index 000000000..5912e814f
--- /dev/null
+++ b/shared/extra-utils/logs/logs-command.ts
@@ -0,0 +1,43 @@
1import { HttpStatusCode } from '@shared/models'
2import { LogLevel } from '../../models/server/log-level.type'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class LogsCommand extends AbstractCommand {
6
7 getLogs (options: OverrideCommandOptions & {
8 startDate: Date
9 endDate?: Date
10 level?: LogLevel
11 }) {
12 const { startDate, endDate, level } = options
13 const path = '/api/v1/server/logs'
14
15 return this.getRequestBody({
16 ...options,
17
18 path,
19 query: { startDate, endDate, level },
20 implicitToken: true,
21 defaultExpectedStatus: HttpStatusCode.OK_200
22 })
23 }
24
25 getAuditLogs (options: OverrideCommandOptions & {
26 startDate: Date
27 endDate?: Date
28 }) {
29 const { startDate, endDate } = options
30
31 const path = '/api/v1/server/audit-logs'
32
33 return this.getRequestBody({
34 ...options,
35
36 path,
37 query: { startDate, endDate },
38 implicitToken: true,
39 defaultExpectedStatus: HttpStatusCode.OK_200
40 })
41 }
42
43}
diff --git a/shared/extra-utils/logs/logs.ts b/shared/extra-utils/logs/logs.ts
deleted file mode 100644
index 8d741276c..000000000
--- a/shared/extra-utils/logs/logs.ts
+++ /dev/null
@@ -1,32 +0,0 @@
1import { makeGetRequest } from '../requests/requests'
2import { LogLevel } from '../../models/server/log-level.type'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function getLogs (url: string, accessToken: string, startDate: Date, endDate?: Date, level?: LogLevel) {
6 const path = '/api/v1/server/logs'
7
8 return makeGetRequest({
9 url,
10 path,
11 token: accessToken,
12 query: { startDate, endDate, level },
13 statusCodeExpected: HttpStatusCode.OK_200
14 })
15}
16
17function getAuditLogs (url: string, accessToken: string, startDate: Date, endDate?: Date) {
18 const path = '/api/v1/server/audit-logs'
19
20 return makeGetRequest({
21 url,
22 path,
23 token: accessToken,
24 query: { startDate, endDate },
25 statusCodeExpected: HttpStatusCode.OK_200
26 })
27}
28
29export {
30 getLogs,
31 getAuditLogs
32}
diff --git a/shared/extra-utils/miscs/checks.ts b/shared/extra-utils/miscs/checks.ts
new file mode 100644
index 000000000..7fc92f804
--- /dev/null
+++ b/shared/extra-utils/miscs/checks.ts
@@ -0,0 +1,46 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2
3import { expect } from 'chai'
4import { pathExists, readFile } from 'fs-extra'
5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { HttpStatusCode } from '@shared/models'
8import { makeGetRequest } from '../requests'
9import { PeerTubeServer } from '../server'
10
11// Default interval -> 5 minutes
12function dateIsValid (dateString: string, interval = 300000) {
13 const dateToCheck = new Date(dateString)
14 const now = new Date()
15
16 return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
17}
18
19async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
20 const res = await makeGetRequest({
21 url,
22 path: imagePath,
23 expectedStatus: HttpStatusCode.OK_200
24 })
25
26 const body = res.body
27
28 const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
29 const minLength = body.length - ((30 * body.length) / 100)
30 const maxLength = body.length + ((30 * body.length) / 100)
31
32 expect(data.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture')
33 expect(data.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture')
34}
35
36async function testFileExistsOrNot (server: PeerTubeServer, directory: string, filePath: string, exist: boolean) {
37 const base = server.servers.buildDirectory(directory)
38
39 expect(await pathExists(join(base, filePath))).to.equal(exist)
40}
41
42export {
43 dateIsValid,
44 testImage,
45 testFileExistsOrNot
46}
diff --git a/shared/extra-utils/miscs/generate.ts b/shared/extra-utils/miscs/generate.ts
new file mode 100644
index 000000000..8d6435481
--- /dev/null
+++ b/shared/extra-utils/miscs/generate.ts
@@ -0,0 +1,61 @@
1import * as ffmpeg from 'fluent-ffmpeg'
2import { ensureDir, pathExists } from 'fs-extra'
3import { dirname } from 'path'
4import { buildAbsoluteFixturePath } from './tests'
5
6async function generateHighBitrateVideo () {
7 const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
8
9 await ensureDir(dirname(tempFixturePath))
10
11 const exists = await pathExists(tempFixturePath)
12 if (!exists) {
13 console.log('Generating high bitrate video.')
14
15 // Generate a random, high bitrate video on the fly, so we don't have to include
16 // a large file in the repo. The video needs to have a certain minimum length so
17 // that FFmpeg properly applies bitrate limits.
18 // https://stackoverflow.com/a/15795112
19 return new Promise<string>((res, rej) => {
20 ffmpeg()
21 .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
22 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
23 .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
24 .output(tempFixturePath)
25 .on('error', rej)
26 .on('end', () => res(tempFixturePath))
27 .run()
28 })
29 }
30
31 return tempFixturePath
32}
33
34async function generateVideoWithFramerate (fps = 60) {
35 const tempFixturePath = buildAbsoluteFixturePath(`video_${fps}fps.mp4`, true)
36
37 await ensureDir(dirname(tempFixturePath))
38
39 const exists = await pathExists(tempFixturePath)
40 if (!exists) {
41 console.log('Generating video with framerate %d.', fps)
42
43 return new Promise<string>((res, rej) => {
44 ffmpeg()
45 .outputOptions([ '-f rawvideo', '-video_size 1280x720', '-i /dev/urandom' ])
46 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
47 .outputOptions([ `-r ${fps}` ])
48 .output(tempFixturePath)
49 .on('error', rej)
50 .on('end', () => res(tempFixturePath))
51 .run()
52 })
53 }
54
55 return tempFixturePath
56}
57
58export {
59 generateHighBitrateVideo,
60 generateVideoWithFramerate
61}
diff --git a/shared/extra-utils/miscs/index.ts b/shared/extra-utils/miscs/index.ts
new file mode 100644
index 000000000..4474661de
--- /dev/null
+++ b/shared/extra-utils/miscs/index.ts
@@ -0,0 +1,5 @@
1export * from './checks'
2export * from './generate'
3export * from './sql-command'
4export * from './tests'
5export * from './webtorrent'
diff --git a/shared/extra-utils/miscs/miscs.ts b/shared/extra-utils/miscs/miscs.ts
deleted file mode 100644
index 462b914d4..000000000
--- a/shared/extra-utils/miscs/miscs.ts
+++ /dev/null
@@ -1,170 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import * as chai from 'chai'
4import * as ffmpeg from 'fluent-ffmpeg'
5import { ensureDir, pathExists, readFile, stat } from 'fs-extra'
6import { basename, dirname, isAbsolute, join, resolve } from 'path'
7import * as request from 'supertest'
8import * as WebTorrent from 'webtorrent'
9import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
10
11const expect = chai.expect
12let webtorrent: WebTorrent.Instance
13
14function immutableAssign<T, U> (target: T, source: U) {
15 return Object.assign<{}, T, U>({}, target, source)
16}
17
18// Default interval -> 5 minutes
19function dateIsValid (dateString: string, interval = 300000) {
20 const dateToCheck = new Date(dateString)
21 const now = new Date()
22
23 return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
24}
25
26function wait (milliseconds: number) {
27 return new Promise(resolve => setTimeout(resolve, milliseconds))
28}
29
30function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
31 const WebTorrent = require('webtorrent')
32
33 if (!webtorrent) webtorrent = new WebTorrent()
34 if (refreshWebTorrent === true) webtorrent = new WebTorrent()
35
36 return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
37}
38
39function root () {
40 // We are in /miscs
41 let root = join(__dirname, '..', '..', '..')
42
43 if (basename(root) === 'dist') root = resolve(root, '..')
44
45 return root
46}
47
48function buildServerDirectory (server: { internalServerNumber: number }, directory: string) {
49 return join(root(), 'test' + server.internalServerNumber, directory)
50}
51
52async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
53 const res = await request(url)
54 .get(imagePath)
55 .expect(HttpStatusCode.OK_200)
56
57 const body = res.body
58
59 const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
60 const minLength = body.length - ((30 * body.length) / 100)
61 const maxLength = body.length + ((30 * body.length) / 100)
62
63 expect(data.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture')
64 expect(data.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture')
65}
66
67async function testFileExistsOrNot (server: { internalServerNumber: number }, directory: string, filePath: string, exist: boolean) {
68 const base = buildServerDirectory(server, directory)
69
70 expect(await pathExists(join(base, filePath))).to.equal(exist)
71}
72
73function isGithubCI () {
74 return !!process.env.GITHUB_WORKSPACE
75}
76
77function buildAbsoluteFixturePath (path: string, customCIPath = false) {
78 if (isAbsolute(path)) return path
79
80 if (customCIPath && process.env.GITHUB_WORKSPACE) {
81 return join(process.env.GITHUB_WORKSPACE, 'fixtures', path)
82 }
83
84 return join(root(), 'server', 'tests', 'fixtures', path)
85}
86
87function areHttpImportTestsDisabled () {
88 const disabled = process.env.DISABLE_HTTP_IMPORT_TESTS === 'true'
89
90 if (disabled) console.log('Import tests are disabled')
91
92 return disabled
93}
94
95async function generateHighBitrateVideo () {
96 const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
97
98 await ensureDir(dirname(tempFixturePath))
99
100 const exists = await pathExists(tempFixturePath)
101 if (!exists) {
102 console.log('Generating high bitrate video.')
103
104 // Generate a random, high bitrate video on the fly, so we don't have to include
105 // a large file in the repo. The video needs to have a certain minimum length so
106 // that FFmpeg properly applies bitrate limits.
107 // https://stackoverflow.com/a/15795112
108 return new Promise<string>((res, rej) => {
109 ffmpeg()
110 .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
111 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
112 .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
113 .output(tempFixturePath)
114 .on('error', rej)
115 .on('end', () => res(tempFixturePath))
116 .run()
117 })
118 }
119
120 return tempFixturePath
121}
122
123async function generateVideoWithFramerate (fps = 60) {
124 const tempFixturePath = buildAbsoluteFixturePath(`video_${fps}fps.mp4`, true)
125
126 await ensureDir(dirname(tempFixturePath))
127
128 const exists = await pathExists(tempFixturePath)
129 if (!exists) {
130 console.log('Generating video with framerate %d.', fps)
131
132 return new Promise<string>((res, rej) => {
133 ffmpeg()
134 .outputOptions([ '-f rawvideo', '-video_size 1280x720', '-i /dev/urandom' ])
135 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
136 .outputOptions([ `-r ${fps}` ])
137 .output(tempFixturePath)
138 .on('error', rej)
139 .on('end', () => res(tempFixturePath))
140 .run()
141 })
142 }
143
144 return tempFixturePath
145}
146
147async function getFileSize (path: string) {
148 const stats = await stat(path)
149
150 return stats.size
151}
152
153// ---------------------------------------------------------------------------
154
155export {
156 dateIsValid,
157 wait,
158 areHttpImportTestsDisabled,
159 buildServerDirectory,
160 webtorrentAdd,
161 getFileSize,
162 immutableAssign,
163 testImage,
164 isGithubCI,
165 buildAbsoluteFixturePath,
166 testFileExistsOrNot,
167 root,
168 generateHighBitrateVideo,
169 generateVideoWithFramerate
170}
diff --git a/shared/extra-utils/miscs/sql-command.ts b/shared/extra-utils/miscs/sql-command.ts
new file mode 100644
index 000000000..80c8cd271
--- /dev/null
+++ b/shared/extra-utils/miscs/sql-command.ts
@@ -0,0 +1,142 @@
1import { QueryTypes, Sequelize } from 'sequelize'
2import { AbstractCommand } from '../shared/abstract-command'
3
4export class SQLCommand extends AbstractCommand {
5 private sequelize: Sequelize
6
7 deleteAll (table: string) {
8 const seq = this.getSequelize()
9
10 const options = { type: QueryTypes.DELETE }
11
12 return seq.query(`DELETE FROM "${table}"`, options)
13 }
14
15 async getCount (table: string) {
16 const seq = this.getSequelize()
17
18 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
19
20 const [ { total } ] = await seq.query<{ total: string }>(`SELECT COUNT(*) as total FROM "${table}"`, options)
21 if (total === null) return 0
22
23 return parseInt(total, 10)
24 }
25
26 setActorField (to: string, field: string, value: string) {
27 const seq = this.getSequelize()
28
29 const options = { type: QueryTypes.UPDATE }
30
31 return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options)
32 }
33
34 setVideoField (uuid: string, field: string, value: string) {
35 const seq = this.getSequelize()
36
37 const options = { type: QueryTypes.UPDATE }
38
39 return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
40 }
41
42 setPlaylistField (uuid: string, field: string, value: string) {
43 const seq = this.getSequelize()
44
45 const options = { type: QueryTypes.UPDATE }
46
47 return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
48 }
49
50 async countVideoViewsOf (uuid: string) {
51 const seq = this.getSequelize()
52
53 // tslint:disable
54 const query = 'SELECT SUM("videoView"."views") AS "total" FROM "videoView" ' +
55 `INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'`
56
57 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
58 const [ { total } ] = await seq.query<{ total: number }>(query, options)
59
60 if (!total) return 0
61
62 return parseInt(total + '', 10)
63 }
64
65 getActorImage (filename: string) {
66 return this.selectQuery(`SELECT * FROM "actorImage" WHERE filename = '${filename}'`)
67 .then(rows => rows[0])
68 }
69
70 selectQuery (query: string) {
71 const seq = this.getSequelize()
72 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
73
74 return seq.query<any>(query, options)
75 }
76
77 updateQuery (query: string) {
78 const seq = this.getSequelize()
79 const options = { type: QueryTypes.UPDATE as QueryTypes.UPDATE }
80
81 return seq.query(query, options)
82 }
83
84 setPluginField (pluginName: string, field: string, value: string) {
85 const seq = this.getSequelize()
86
87 const options = { type: QueryTypes.UPDATE }
88
89 return seq.query(`UPDATE "plugin" SET "${field}" = '${value}' WHERE "name" = '${pluginName}'`, options)
90 }
91
92 setPluginVersion (pluginName: string, newVersion: string) {
93 return this.setPluginField(pluginName, 'version', newVersion)
94 }
95
96 setPluginLatestVersion (pluginName: string, newVersion: string) {
97 return this.setPluginField(pluginName, 'latestVersion', newVersion)
98 }
99
100 setActorFollowScores (newScore: number) {
101 const seq = this.getSequelize()
102
103 const options = { type: QueryTypes.UPDATE }
104
105 return seq.query(`UPDATE "actorFollow" SET "score" = ${newScore}`, options)
106 }
107
108 setTokenField (accessToken: string, field: string, value: string) {
109 const seq = this.getSequelize()
110
111 const options = { type: QueryTypes.UPDATE }
112
113 return seq.query(`UPDATE "oAuthToken" SET "${field}" = '${value}' WHERE "accessToken" = '${accessToken}'`, options)
114 }
115
116 async cleanup () {
117 if (!this.sequelize) return
118
119 await this.sequelize.close()
120 this.sequelize = undefined
121 }
122
123 private getSequelize () {
124 if (this.sequelize) return this.sequelize
125
126 const dbname = 'peertube_test' + this.server.internalServerNumber
127 const username = 'peertube'
128 const password = 'peertube'
129 const host = 'localhost'
130 const port = 5432
131
132 this.sequelize = new Sequelize(dbname, username, password, {
133 dialect: 'postgres',
134 host,
135 port,
136 logging: false
137 })
138
139 return this.sequelize
140 }
141
142}
diff --git a/shared/extra-utils/miscs/sql.ts b/shared/extra-utils/miscs/sql.ts
deleted file mode 100644
index 65a0aa5fe..000000000
--- a/shared/extra-utils/miscs/sql.ts
+++ /dev/null
@@ -1,161 +0,0 @@
1import { QueryTypes, Sequelize } from 'sequelize'
2import { ServerInfo } from '../server/servers'
3
4const sequelizes: { [ id: number ]: Sequelize } = {}
5
6function getSequelize (internalServerNumber: number) {
7 if (sequelizes[internalServerNumber]) return sequelizes[internalServerNumber]
8
9 const dbname = 'peertube_test' + internalServerNumber
10 const username = 'peertube'
11 const password = 'peertube'
12 const host = 'localhost'
13 const port = 5432
14
15 const seq = new Sequelize(dbname, username, password, {
16 dialect: 'postgres',
17 host,
18 port,
19 logging: false
20 })
21
22 sequelizes[internalServerNumber] = seq
23
24 return seq
25}
26
27function deleteAll (internalServerNumber: number, table: string) {
28 const seq = getSequelize(internalServerNumber)
29
30 const options = { type: QueryTypes.DELETE }
31
32 return seq.query(`DELETE FROM "${table}"`, options)
33}
34
35async function getCount (internalServerNumber: number, table: string) {
36 const seq = getSequelize(internalServerNumber)
37
38 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
39
40 const [ { total } ] = await seq.query<{ total: string }>(`SELECT COUNT(*) as total FROM "${table}"`, options)
41 if (total === null) return 0
42
43 return parseInt(total, 10)
44}
45
46function setActorField (internalServerNumber: number, to: string, field: string, value: string) {
47 const seq = getSequelize(internalServerNumber)
48
49 const options = { type: QueryTypes.UPDATE }
50
51 return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options)
52}
53
54function setVideoField (internalServerNumber: number, uuid: string, field: string, value: string) {
55 const seq = getSequelize(internalServerNumber)
56
57 const options = { type: QueryTypes.UPDATE }
58
59 return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
60}
61
62function setPlaylistField (internalServerNumber: number, uuid: string, field: string, value: string) {
63 const seq = getSequelize(internalServerNumber)
64
65 const options = { type: QueryTypes.UPDATE }
66
67 return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
68}
69
70async function countVideoViewsOf (internalServerNumber: number, uuid: string) {
71 const seq = getSequelize(internalServerNumber)
72
73 // tslint:disable
74 const query = 'SELECT SUM("videoView"."views") AS "total" FROM "videoView" ' +
75 `INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'`
76
77 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
78 const [ { total } ] = await seq.query<{ total: number }>(query, options)
79
80 if (!total) return 0
81
82 return parseInt(total + '', 10)
83}
84
85function getActorImage (internalServerNumber: number, filename: string) {
86 return selectQuery(internalServerNumber, `SELECT * FROM "actorImage" WHERE filename = '${filename}'`)
87 .then(rows => rows[0])
88}
89
90function selectQuery (internalServerNumber: number, query: string) {
91 const seq = getSequelize(internalServerNumber)
92 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
93
94 return seq.query<any>(query, options)
95}
96
97function updateQuery (internalServerNumber: number, query: string) {
98 const seq = getSequelize(internalServerNumber)
99 const options = { type: QueryTypes.UPDATE as QueryTypes.UPDATE }
100
101 return seq.query(query, options)
102}
103
104async function closeAllSequelize (servers: ServerInfo[]) {
105 for (const server of servers) {
106 if (sequelizes[server.internalServerNumber]) {
107 await sequelizes[server.internalServerNumber].close()
108 // eslint-disable-next-line
109 delete sequelizes[server.internalServerNumber]
110 }
111 }
112}
113
114function setPluginField (internalServerNumber: number, pluginName: string, field: string, value: string) {
115 const seq = getSequelize(internalServerNumber)
116
117 const options = { type: QueryTypes.UPDATE }
118
119 return seq.query(`UPDATE "plugin" SET "${field}" = '${value}' WHERE "name" = '${pluginName}'`, options)
120}
121
122function setPluginVersion (internalServerNumber: number, pluginName: string, newVersion: string) {
123 return setPluginField(internalServerNumber, pluginName, 'version', newVersion)
124}
125
126function setPluginLatestVersion (internalServerNumber: number, pluginName: string, newVersion: string) {
127 return setPluginField(internalServerNumber, pluginName, 'latestVersion', newVersion)
128}
129
130function setActorFollowScores (internalServerNumber: number, newScore: number) {
131 const seq = getSequelize(internalServerNumber)
132
133 const options = { type: QueryTypes.UPDATE }
134
135 return seq.query(`UPDATE "actorFollow" SET "score" = ${newScore}`, options)
136}
137
138function setTokenField (internalServerNumber: number, accessToken: string, field: string, value: string) {
139 const seq = getSequelize(internalServerNumber)
140
141 const options = { type: QueryTypes.UPDATE }
142
143 return seq.query(`UPDATE "oAuthToken" SET "${field}" = '${value}' WHERE "accessToken" = '${accessToken}'`, options)
144}
145
146export {
147 setVideoField,
148 setPlaylistField,
149 setActorField,
150 countVideoViewsOf,
151 setPluginVersion,
152 setPluginLatestVersion,
153 selectQuery,
154 getActorImage,
155 deleteAll,
156 setTokenField,
157 updateQuery,
158 setActorFollowScores,
159 closeAllSequelize,
160 getCount
161}
diff --git a/shared/extra-utils/miscs/stubs.ts b/shared/extra-utils/miscs/stubs.ts
deleted file mode 100644
index d1eb0e3b2..000000000
--- a/shared/extra-utils/miscs/stubs.ts
+++ /dev/null
@@ -1,14 +0,0 @@
1function buildRequestStub (): any {
2 return { }
3}
4
5function buildResponseStub (): any {
6 return {
7 locals: {}
8 }
9}
10
11export {
12 buildResponseStub,
13 buildRequestStub
14}
diff --git a/shared/extra-utils/miscs/tests.ts b/shared/extra-utils/miscs/tests.ts
new file mode 100644
index 000000000..3dfb2487e
--- /dev/null
+++ b/shared/extra-utils/miscs/tests.ts
@@ -0,0 +1,94 @@
1import { stat } from 'fs-extra'
2import { basename, isAbsolute, join, resolve } from 'path'
3
4const FIXTURE_URLS = {
5 youtube: 'https://www.youtube.com/watch?v=msX3jv1XdvM',
6
7 /**
8 * The video is used to check format-selection correctness wrt. HDR,
9 * which brings its own set of oddities outside of a MediaSource.
10 * FIXME: refactor once HDR is supported at playback
11 *
12 * The video needs to have the following format_ids:
13 * (which you can check by using `youtube-dl <url> -F`):
14 * - 303 (1080p webm vp9)
15 * - 299 (1080p mp4 avc1)
16 * - 335 (1080p webm vp9.2 HDR)
17 *
18 * 15 jan. 2021: TEST VIDEO NOT CURRENTLY PROVIDING
19 * - 400 (1080p mp4 av01)
20 * - 315 (2160p webm vp9 HDR)
21 * - 337 (2160p webm vp9.2 HDR)
22 * - 401 (2160p mp4 av01 HDR)
23 */
24 youtubeHDR: 'https://www.youtube.com/watch?v=qR5vOXbZsI4',
25
26 // eslint-disable-next-line max-len
27 magnet: 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4',
28
29 badVideo: 'https://download.cpy.re/peertube/bad_video.mp4',
30 goodVideo: 'https://download.cpy.re/peertube/good_video.mp4',
31 video4K: 'https://download.cpy.re/peertube/4k_file.txt'
32}
33
34function parallelTests () {
35 return process.env.MOCHA_PARALLEL === 'true'
36}
37
38function isGithubCI () {
39 return !!process.env.GITHUB_WORKSPACE
40}
41
42function areHttpImportTestsDisabled () {
43 const disabled = process.env.DISABLE_HTTP_IMPORT_TESTS === 'true'
44
45 if (disabled) console.log('Import tests are disabled')
46
47 return disabled
48}
49
50function buildAbsoluteFixturePath (path: string, customCIPath = false) {
51 if (isAbsolute(path)) return path
52
53 if (customCIPath && process.env.GITHUB_WORKSPACE) {
54 return join(process.env.GITHUB_WORKSPACE, 'fixtures', path)
55 }
56
57 return join(root(), 'server', 'tests', 'fixtures', path)
58}
59
60function root () {
61 // We are in /miscs
62 let root = join(__dirname, '..', '..', '..')
63
64 if (basename(root) === 'dist') root = resolve(root, '..')
65
66 return root
67}
68
69function wait (milliseconds: number) {
70 return new Promise(resolve => setTimeout(resolve, milliseconds))
71}
72
73async function getFileSize (path: string) {
74 const stats = await stat(path)
75
76 return stats.size
77}
78
79function buildRequestStub (): any {
80 return { }
81}
82
83export {
84 FIXTURE_URLS,
85
86 parallelTests,
87 isGithubCI,
88 areHttpImportTestsDisabled,
89 buildAbsoluteFixturePath,
90 getFileSize,
91 buildRequestStub,
92 wait,
93 root
94}
diff --git a/shared/extra-utils/miscs/webtorrent.ts b/shared/extra-utils/miscs/webtorrent.ts
new file mode 100644
index 000000000..815ea3d56
--- /dev/null
+++ b/shared/extra-utils/miscs/webtorrent.ts
@@ -0,0 +1,30 @@
1import { readFile } from 'fs-extra'
2import * as parseTorrent from 'parse-torrent'
3import { join } from 'path'
4import * as WebTorrent from 'webtorrent'
5import { PeerTubeServer } from '../server'
6
7let webtorrent: WebTorrent.Instance
8
9function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
10 const WebTorrent = require('webtorrent')
11
12 if (!webtorrent) webtorrent = new WebTorrent()
13 if (refreshWebTorrent === true) webtorrent = new WebTorrent()
14
15 return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
16}
17
18async function parseTorrentVideo (server: PeerTubeServer, videoUUID: string, resolution: number) {
19 const torrentName = videoUUID + '-' + resolution + '.torrent'
20 const torrentPath = server.servers.buildDirectory(join('torrents', torrentName))
21
22 const data = await readFile(torrentPath)
23
24 return parseTorrent(data)
25}
26
27export {
28 webtorrentAdd,
29 parseTorrentVideo
30}
diff --git a/shared/extra-utils/mock-servers/index.ts b/shared/extra-utils/mock-servers/index.ts
new file mode 100644
index 000000000..0ec07f685
--- /dev/null
+++ b/shared/extra-utils/mock-servers/index.ts
@@ -0,0 +1,4 @@
1export * from './mock-email'
2export * from './mock-instances-index'
3export * from './mock-joinpeertube-versions'
4export * from './mock-plugin-blocklist'
diff --git a/shared/extra-utils/miscs/email.ts b/shared/extra-utils/mock-servers/mock-email.ts
index 9fc9a5ad0..ffd62e325 100644
--- a/shared/extra-utils/miscs/email.ts
+++ b/shared/extra-utils/mock-servers/mock-email.ts
@@ -1,6 +1,6 @@
1import { ChildProcess } from 'child_process' 1import { ChildProcess } from 'child_process'
2import { randomInt } from '../../core-utils/miscs/miscs' 2import { randomInt } from '@shared/core-utils'
3import { parallelTests } from '../server/servers' 3import { parallelTests } from '../miscs'
4 4
5const MailDev = require('maildev') 5const MailDev = require('maildev')
6 6
diff --git a/shared/extra-utils/mock-servers/joinpeertube-versions.ts b/shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts
index 5ea432ecf..5ea432ecf 100644
--- a/shared/extra-utils/mock-servers/joinpeertube-versions.ts
+++ b/shared/extra-utils/mock-servers/mock-joinpeertube-versions.ts
diff --git a/shared/extra-utils/plugins/mock-blocklist.ts b/shared/extra-utils/mock-servers/mock-plugin-blocklist.ts
index d18f8224f..d18f8224f 100644
--- a/shared/extra-utils/plugins/mock-blocklist.ts
+++ b/shared/extra-utils/mock-servers/mock-plugin-blocklist.ts
diff --git a/shared/extra-utils/moderation/abuses-command.ts b/shared/extra-utils/moderation/abuses-command.ts
new file mode 100644
index 000000000..7b3abb056
--- /dev/null
+++ b/shared/extra-utils/moderation/abuses-command.ts
@@ -0,0 +1,228 @@
1import { pick } from 'lodash'
2import {
3 AbuseFilter,
4 AbuseMessage,
5 AbusePredefinedReasonsString,
6 AbuseState,
7 AbuseUpdate,
8 AbuseVideoIs,
9 AdminAbuse,
10 HttpStatusCode,
11 ResultList,
12 UserAbuse
13} from '@shared/models'
14import { unwrapBody } from '../requests/requests'
15import { AbstractCommand, OverrideCommandOptions } from '../shared'
16
17export class AbusesCommand extends AbstractCommand {
18
19 report (options: OverrideCommandOptions & {
20 reason: string
21
22 accountId?: number
23 videoId?: number
24 commentId?: number
25
26 predefinedReasons?: AbusePredefinedReasonsString[]
27
28 startAt?: number
29 endAt?: number
30 }) {
31 const path = '/api/v1/abuses'
32
33 const video = options.videoId
34 ? {
35 id: options.videoId,
36 startAt: options.startAt,
37 endAt: options.endAt
38 }
39 : undefined
40
41 const comment = options.commentId
42 ? { id: options.commentId }
43 : undefined
44
45 const account = options.accountId
46 ? { id: options.accountId }
47 : undefined
48
49 const body = {
50 account,
51 video,
52 comment,
53
54 reason: options.reason,
55 predefinedReasons: options.predefinedReasons
56 }
57
58 return unwrapBody<{ abuse: { id: number } }>(this.postBodyRequest({
59 ...options,
60
61 path,
62 fields: body,
63 implicitToken: true,
64 defaultExpectedStatus: HttpStatusCode.OK_200
65 }))
66 }
67
68 getAdminList (options: OverrideCommandOptions & {
69 start?: number
70 count?: number
71 sort?: string
72
73 id?: number
74 predefinedReason?: AbusePredefinedReasonsString
75 search?: string
76 filter?: AbuseFilter
77 state?: AbuseState
78 videoIs?: AbuseVideoIs
79 searchReporter?: string
80 searchReportee?: string
81 searchVideo?: string
82 searchVideoChannel?: string
83 } = {}) {
84 const toPick = [
85 'count',
86 'filter',
87 'id',
88 'predefinedReason',
89 'search',
90 'searchReportee',
91 'searchReporter',
92 'searchVideo',
93 'searchVideoChannel',
94 'sort',
95 'start',
96 'state',
97 'videoIs'
98 ]
99
100 const path = '/api/v1/abuses'
101
102 const defaultQuery = { sort: 'createdAt' }
103 const query = { ...defaultQuery, ...pick(options, toPick) }
104
105 return this.getRequestBody<ResultList<AdminAbuse>>({
106 ...options,
107
108 path,
109 query,
110 implicitToken: true,
111 defaultExpectedStatus: HttpStatusCode.OK_200
112 })
113 }
114
115 getUserList (options: OverrideCommandOptions & {
116 start?: number
117 count?: number
118 sort?: string
119
120 id?: number
121 search?: string
122 state?: AbuseState
123 }) {
124 const toPick = [
125 'id',
126 'search',
127 'state',
128 'start',
129 'count',
130 'sort'
131 ]
132
133 const path = '/api/v1/users/me/abuses'
134
135 const defaultQuery = { sort: 'createdAt' }
136 const query = { ...defaultQuery, ...pick(options, toPick) }
137
138 return this.getRequestBody<ResultList<UserAbuse>>({
139 ...options,
140
141 path,
142 query,
143 implicitToken: true,
144 defaultExpectedStatus: HttpStatusCode.OK_200
145 })
146 }
147
148 update (options: OverrideCommandOptions & {
149 abuseId: number
150 body: AbuseUpdate
151 }) {
152 const { abuseId, body } = options
153 const path = '/api/v1/abuses/' + abuseId
154
155 return this.putBodyRequest({
156 ...options,
157
158 path,
159 fields: body,
160 implicitToken: true,
161 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
162 })
163 }
164
165 delete (options: OverrideCommandOptions & {
166 abuseId: number
167 }) {
168 const { abuseId } = options
169 const path = '/api/v1/abuses/' + abuseId
170
171 return this.deleteRequest({
172 ...options,
173
174 path,
175 implicitToken: true,
176 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
177 })
178 }
179
180 listMessages (options: OverrideCommandOptions & {
181 abuseId: number
182 }) {
183 const { abuseId } = options
184 const path = '/api/v1/abuses/' + abuseId + '/messages'
185
186 return this.getRequestBody<ResultList<AbuseMessage>>({
187 ...options,
188
189 path,
190 implicitToken: true,
191 defaultExpectedStatus: HttpStatusCode.OK_200
192 })
193 }
194
195 deleteMessage (options: OverrideCommandOptions & {
196 abuseId: number
197 messageId: number
198 }) {
199 const { abuseId, messageId } = options
200 const path = '/api/v1/abuses/' + abuseId + '/messages/' + messageId
201
202 return this.deleteRequest({
203 ...options,
204
205 path,
206 implicitToken: true,
207 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
208 })
209 }
210
211 addMessage (options: OverrideCommandOptions & {
212 abuseId: number
213 message: string
214 }) {
215 const { abuseId, message } = options
216 const path = '/api/v1/abuses/' + abuseId + '/messages'
217
218 return this.postBodyRequest({
219 ...options,
220
221 path,
222 fields: { message },
223 implicitToken: true,
224 defaultExpectedStatus: HttpStatusCode.OK_200
225 })
226 }
227
228}
diff --git a/shared/extra-utils/moderation/abuses.ts b/shared/extra-utils/moderation/abuses.ts
deleted file mode 100644
index c0fda722f..000000000
--- a/shared/extra-utils/moderation/abuses.ts
+++ /dev/null
@@ -1,244 +0,0 @@
1import { AbuseFilter, AbusePredefinedReasonsString, AbuseState, AbuseUpdate, AbuseVideoIs } from '@shared/models'
2import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function reportAbuse (options: {
6 url: string
7 token: string
8
9 reason: string
10
11 accountId?: number
12 videoId?: number
13 commentId?: number
14
15 predefinedReasons?: AbusePredefinedReasonsString[]
16
17 startAt?: number
18 endAt?: number
19
20 statusCodeExpected?: number
21}) {
22 const path = '/api/v1/abuses'
23
24 const video = options.videoId
25 ? {
26 id: options.videoId,
27 startAt: options.startAt,
28 endAt: options.endAt
29 }
30 : undefined
31
32 const comment = options.commentId
33 ? { id: options.commentId }
34 : undefined
35
36 const account = options.accountId
37 ? { id: options.accountId }
38 : undefined
39
40 const body = {
41 account,
42 video,
43 comment,
44
45 reason: options.reason,
46 predefinedReasons: options.predefinedReasons
47 }
48
49 return makePostBodyRequest({
50 url: options.url,
51 path,
52 token: options.token,
53
54 fields: body,
55 statusCodeExpected: options.statusCodeExpected || HttpStatusCode.OK_200
56 })
57}
58
59function getAdminAbusesList (options: {
60 url: string
61 token: string
62
63 start?: number
64 count?: number
65 sort?: string
66
67 id?: number
68 predefinedReason?: AbusePredefinedReasonsString
69 search?: string
70 filter?: AbuseFilter
71 state?: AbuseState
72 videoIs?: AbuseVideoIs
73 searchReporter?: string
74 searchReportee?: string
75 searchVideo?: string
76 searchVideoChannel?: string
77}) {
78 const {
79 url,
80 token,
81 start,
82 count,
83 sort,
84 id,
85 predefinedReason,
86 search,
87 filter,
88 state,
89 videoIs,
90 searchReporter,
91 searchReportee,
92 searchVideo,
93 searchVideoChannel
94 } = options
95 const path = '/api/v1/abuses'
96
97 const query = {
98 id,
99 predefinedReason,
100 search,
101 state,
102 filter,
103 videoIs,
104 start,
105 count,
106 sort: sort || 'createdAt',
107 searchReporter,
108 searchReportee,
109 searchVideo,
110 searchVideoChannel
111 }
112
113 return makeGetRequest({
114 url,
115 path,
116 token,
117 query,
118 statusCodeExpected: HttpStatusCode.OK_200
119 })
120}
121
122function getUserAbusesList (options: {
123 url: string
124 token: string
125
126 start?: number
127 count?: number
128 sort?: string
129
130 id?: number
131 search?: string
132 state?: AbuseState
133}) {
134 const {
135 url,
136 token,
137 start,
138 count,
139 sort,
140 id,
141 search,
142 state
143 } = options
144 const path = '/api/v1/users/me/abuses'
145
146 const query = {
147 id,
148 search,
149 state,
150 start,
151 count,
152 sort: sort || 'createdAt'
153 }
154
155 return makeGetRequest({
156 url,
157 path,
158 token,
159 query,
160 statusCodeExpected: HttpStatusCode.OK_200
161 })
162}
163
164function updateAbuse (
165 url: string,
166 token: string,
167 abuseId: number,
168 body: AbuseUpdate,
169 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
170) {
171 const path = '/api/v1/abuses/' + abuseId
172
173 return makePutBodyRequest({
174 url,
175 token,
176 path,
177 fields: body,
178 statusCodeExpected
179 })
180}
181
182function deleteAbuse (url: string, token: string, abuseId: number, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
183 const path = '/api/v1/abuses/' + abuseId
184
185 return makeDeleteRequest({
186 url,
187 token,
188 path,
189 statusCodeExpected
190 })
191}
192
193function listAbuseMessages (url: string, token: string, abuseId: number, statusCodeExpected = HttpStatusCode.OK_200) {
194 const path = '/api/v1/abuses/' + abuseId + '/messages'
195
196 return makeGetRequest({
197 url,
198 token,
199 path,
200 statusCodeExpected
201 })
202}
203
204function deleteAbuseMessage (
205 url: string,
206 token: string,
207 abuseId: number,
208 messageId: number,
209 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
210) {
211 const path = '/api/v1/abuses/' + abuseId + '/messages/' + messageId
212
213 return makeDeleteRequest({
214 url,
215 token,
216 path,
217 statusCodeExpected
218 })
219}
220
221function addAbuseMessage (url: string, token: string, abuseId: number, message: string, statusCodeExpected = HttpStatusCode.OK_200) {
222 const path = '/api/v1/abuses/' + abuseId + '/messages'
223
224 return makePostBodyRequest({
225 url,
226 token,
227 path,
228 fields: { message },
229 statusCodeExpected
230 })
231}
232
233// ---------------------------------------------------------------------------
234
235export {
236 reportAbuse,
237 getAdminAbusesList,
238 updateAbuse,
239 deleteAbuse,
240 getUserAbusesList,
241 listAbuseMessages,
242 deleteAbuseMessage,
243 addAbuseMessage
244}
diff --git a/shared/extra-utils/moderation/index.ts b/shared/extra-utils/moderation/index.ts
new file mode 100644
index 000000000..b37643956
--- /dev/null
+++ b/shared/extra-utils/moderation/index.ts
@@ -0,0 +1 @@
export * from './abuses-command'
diff --git a/shared/extra-utils/overviews/index.ts b/shared/extra-utils/overviews/index.ts
new file mode 100644
index 000000000..e19551907
--- /dev/null
+++ b/shared/extra-utils/overviews/index.ts
@@ -0,0 +1 @@
export * from './overviews-command'
diff --git a/shared/extra-utils/overviews/overviews-command.ts b/shared/extra-utils/overviews/overviews-command.ts
new file mode 100644
index 000000000..06b4892d2
--- /dev/null
+++ b/shared/extra-utils/overviews/overviews-command.ts
@@ -0,0 +1,23 @@
1import { HttpStatusCode, VideosOverview } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class OverviewsCommand extends AbstractCommand {
5
6 getVideos (options: OverrideCommandOptions & {
7 page: number
8 }) {
9 const { page } = options
10 const path = '/api/v1/overviews/videos'
11
12 const query = { page }
13
14 return this.getRequestBody<VideosOverview>({
15 ...options,
16
17 path,
18 query,
19 implicitToken: false,
20 defaultExpectedStatus: HttpStatusCode.OK_200
21 })
22 }
23}
diff --git a/shared/extra-utils/overviews/overviews.ts b/shared/extra-utils/overviews/overviews.ts
deleted file mode 100644
index 5e1a13e5e..000000000
--- a/shared/extra-utils/overviews/overviews.ts
+++ /dev/null
@@ -1,34 +0,0 @@
1import { makeGetRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function getVideosOverview (url: string, page: number, statusCodeExpected = HttpStatusCode.OK_200) {
5 const path = '/api/v1/overviews/videos'
6
7 const query = { page }
8
9 return makeGetRequest({
10 url,
11 path,
12 query,
13 statusCodeExpected
14 })
15}
16
17function getVideosOverviewWithToken (url: string, page: number, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
18 const path = '/api/v1/overviews/videos'
19
20 const query = { page }
21
22 return makeGetRequest({
23 url,
24 path,
25 query,
26 token,
27 statusCodeExpected
28 })
29}
30
31export {
32 getVideosOverview,
33 getVideosOverviewWithToken
34}
diff --git a/shared/extra-utils/requests/activitypub.ts b/shared/extra-utils/requests/activitypub.ts
index ecd8ce823..4ae878384 100644
--- a/shared/extra-utils/requests/activitypub.ts
+++ b/shared/extra-utils/requests/activitypub.ts
@@ -1,7 +1,7 @@
1import { activityPubContextify } from '../../../server/helpers/activitypub'
1import { doRequest } from '../../../server/helpers/requests' 2import { doRequest } from '../../../server/helpers/requests'
2import { HTTP_SIGNATURE } from '../../../server/initializers/constants' 3import { HTTP_SIGNATURE } from '../../../server/initializers/constants'
3import { buildGlobalHeaders } from '../../../server/lib/job-queue/handlers/utils/activitypub-http-utils' 4import { buildGlobalHeaders } from '../../../server/lib/job-queue/handlers/utils/activitypub-http-utils'
4import { activityPubContextify } from '../../../server/helpers/activitypub'
5 5
6function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) { 6function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
7 const options = { 7 const options = {
diff --git a/shared/extra-utils/requests/check-api-params.ts b/shared/extra-utils/requests/check-api-params.ts
index 7f5ff775c..26ba1e913 100644
--- a/shared/extra-utils/requests/check-api-params.ts
+++ b/shared/extra-utils/requests/check-api-params.ts
@@ -1,14 +1,13 @@
1import { HttpStatusCode } from '@shared/models'
1import { makeGetRequest } from './requests' 2import { makeGetRequest } from './requests'
2import { immutableAssign } from '../miscs/miscs'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4 3
5function checkBadStartPagination (url: string, path: string, token?: string, query = {}) { 4function checkBadStartPagination (url: string, path: string, token?: string, query = {}) {
6 return makeGetRequest({ 5 return makeGetRequest({
7 url, 6 url,
8 path, 7 path,
9 token, 8 token,
10 query: immutableAssign(query, { start: 'hello' }), 9 query: { ...query, start: 'hello' },
11 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 10 expectedStatus: HttpStatusCode.BAD_REQUEST_400
12 }) 11 })
13} 12}
14 13
@@ -17,16 +16,16 @@ async function checkBadCountPagination (url: string, path: string, token?: strin
17 url, 16 url,
18 path, 17 path,
19 token, 18 token,
20 query: immutableAssign(query, { count: 'hello' }), 19 query: { ...query, count: 'hello' },
21 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 20 expectedStatus: HttpStatusCode.BAD_REQUEST_400
22 }) 21 })
23 22
24 await makeGetRequest({ 23 await makeGetRequest({
25 url, 24 url,
26 path, 25 path,
27 token, 26 token,
28 query: immutableAssign(query, { count: 2000 }), 27 query: { ...query, count: 2000 },
29 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 28 expectedStatus: HttpStatusCode.BAD_REQUEST_400
30 }) 29 })
31} 30}
32 31
@@ -35,8 +34,8 @@ function checkBadSortPagination (url: string, path: string, token?: string, quer
35 url, 34 url,
36 path, 35 path,
37 token, 36 token,
38 query: immutableAssign(query, { sort: 'hello' }), 37 query: { ...query, sort: 'hello' },
39 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 38 expectedStatus: HttpStatusCode.BAD_REQUEST_400
40 }) 39 })
41} 40}
42 41
diff --git a/shared/extra-utils/requests/index.ts b/shared/extra-utils/requests/index.ts
new file mode 100644
index 000000000..501163f92
--- /dev/null
+++ b/shared/extra-utils/requests/index.ts
@@ -0,0 +1,3 @@
1// Don't include activitypub that import stuff from server
2export * from './check-api-params'
3export * from './requests'
diff --git a/shared/extra-utils/requests/requests.ts b/shared/extra-utils/requests/requests.ts
index 38e24d897..70f790222 100644
--- a/shared/extra-utils/requests/requests.ts
+++ b/shared/extra-utils/requests/requests.ts
@@ -1,103 +1,82 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ 1/* eslint-disable @typescript-eslint/no-floating-promises */
2 2
3import { decode } from 'querystring'
3import * as request from 'supertest' 4import * as request from 'supertest'
4import { buildAbsoluteFixturePath, root } from '../miscs/miscs'
5import { isAbsolute, join } from 'path'
6import { URL } from 'url' 5import { URL } from 'url'
7import { decode } from 'querystring' 6import { HttpStatusCode } from '@shared/models'
8import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 7import { buildAbsoluteFixturePath } from '../miscs/tests'
9 8
10function get4KFileUrl () { 9export type CommonRequestParams = {
11 return 'https://download.cpy.re/peertube/4k_file.txt' 10 url: string
11 path?: string
12 contentType?: string
13 range?: string
14 redirects?: number
15 accept?: string
16 host?: string
17 token?: string
18 headers?: { [ name: string ]: string }
19 type?: string
20 xForwardedFor?: string
21 expectedStatus?: HttpStatusCode
12} 22}
13 23
14function makeRawRequest (url: string, statusCodeExpected?: HttpStatusCode, range?: string) { 24function makeRawRequest (url: string, expectedStatus?: HttpStatusCode, range?: string) {
15 const { host, protocol, pathname } = new URL(url) 25 const { host, protocol, pathname } = new URL(url)
16 26
17 return makeGetRequest({ url: `${protocol}//${host}`, path: pathname, statusCodeExpected, range }) 27 return makeGetRequest({ url: `${protocol}//${host}`, path: pathname, expectedStatus, range })
18} 28}
19 29
20function makeGetRequest (options: { 30function makeGetRequest (options: CommonRequestParams & {
21 url: string
22 path?: string
23 query?: any 31 query?: any
24 token?: string
25 statusCodeExpected?: HttpStatusCode
26 contentType?: string
27 range?: string
28 redirects?: number
29 accept?: string
30}) { 32}) {
31 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400
32 if (options.contentType === undefined) options.contentType = 'application/json'
33
34 const req = request(options.url).get(options.path) 33 const req = request(options.url).get(options.path)
34 .query(options.query)
35 35
36 if (options.contentType) req.set('Accept', options.contentType) 36 return buildRequest(req, { contentType: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
37 if (options.token) req.set('Authorization', 'Bearer ' + options.token)
38 if (options.query) req.query(options.query)
39 if (options.range) req.set('Range', options.range)
40 if (options.accept) req.set('Accept', options.accept)
41 if (options.redirects) req.redirects(options.redirects)
42
43 return req.expect(options.statusCodeExpected)
44} 37}
45 38
46function makeDeleteRequest (options: { 39function makeHTMLRequest (url: string, path: string) {
47 url: string 40 return makeGetRequest({
48 path: string 41 url,
49 token?: string 42 path,
50 statusCodeExpected?: HttpStatusCode 43 accept: 'text/html',
51}) { 44 expectedStatus: HttpStatusCode.OK_200
52 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400 45 })
46}
53 47
54 const req = request(options.url) 48function makeActivityPubGetRequest (url: string, path: string, expectedStatus = HttpStatusCode.OK_200) {
55 .delete(options.path) 49 return makeGetRequest({
56 .set('Accept', 'application/json') 50 url,
51 path,
52 expectedStatus: expectedStatus,
53 accept: 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8'
54 })
55}
57 56
58 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 57function makeDeleteRequest (options: CommonRequestParams) {
58 const req = request(options.url).delete(options.path)
59 59
60 return req.expect(options.statusCodeExpected) 60 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
61} 61}
62 62
63function makeUploadRequest (options: { 63function makeUploadRequest (options: CommonRequestParams & {
64 url: string
65 method?: 'POST' | 'PUT' 64 method?: 'POST' | 'PUT'
66 path: string 65
67 token?: string
68 fields: { [ fieldName: string ]: any } 66 fields: { [ fieldName: string ]: any }
69 attaches?: { [ attachName: string ]: any | any[] } 67 attaches?: { [ attachName: string ]: any | any[] }
70 statusCodeExpected?: HttpStatusCode
71}) { 68}) {
72 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400 69 let req = options.method === 'PUT'
73 70 ? request(options.url).put(options.path)
74 let req: request.Test 71 : request(options.url).post(options.path)
75 if (options.method === 'PUT') {
76 req = request(options.url).put(options.path)
77 } else {
78 req = request(options.url).post(options.path)
79 }
80
81 req.set('Accept', 'application/json')
82 72
83 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 73 req = buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
84 74
85 Object.keys(options.fields).forEach(field => { 75 buildFields(req, options.fields)
86 const value = options.fields[field]
87
88 if (value === undefined) return
89
90 if (Array.isArray(value)) {
91 for (let i = 0; i < value.length; i++) {
92 req.field(field + '[' + i + ']', value[i])
93 }
94 } else {
95 req.field(field, value)
96 }
97 })
98 76
99 Object.keys(options.attaches || {}).forEach(attach => { 77 Object.keys(options.attaches || {}).forEach(attach => {
100 const value = options.attaches[attach] 78 const value = options.attaches[attach]
79
101 if (Array.isArray(value)) { 80 if (Array.isArray(value)) {
102 req.attach(attach, buildAbsoluteFixturePath(value[0]), value[1]) 81 req.attach(attach, buildAbsoluteFixturePath(value[0]), value[1])
103 } else { 82 } else {
@@ -105,27 +84,16 @@ function makeUploadRequest (options: {
105 } 84 }
106 }) 85 })
107 86
108 return req.expect(options.statusCodeExpected) 87 return req
109} 88}
110 89
111function makePostBodyRequest (options: { 90function makePostBodyRequest (options: CommonRequestParams & {
112 url: string
113 path: string
114 token?: string
115 fields?: { [ fieldName: string ]: any } 91 fields?: { [ fieldName: string ]: any }
116 statusCodeExpected?: HttpStatusCode
117}) { 92}) {
118 if (!options.fields) options.fields = {} 93 const req = request(options.url).post(options.path)
119 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400 94 .send(options.fields)
120
121 const req = request(options.url)
122 .post(options.path)
123 .set('Accept', 'application/json')
124 95
125 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 96 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
126
127 return req.send(options.fields)
128 .expect(options.statusCodeExpected)
129} 97}
130 98
131function makePutBodyRequest (options: { 99function makePutBodyRequest (options: {
@@ -133,59 +101,29 @@ function makePutBodyRequest (options: {
133 path: string 101 path: string
134 token?: string 102 token?: string
135 fields: { [ fieldName: string ]: any } 103 fields: { [ fieldName: string ]: any }
136 statusCodeExpected?: HttpStatusCode 104 expectedStatus?: HttpStatusCode
137}) { 105}) {
138 if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400 106 const req = request(options.url).put(options.path)
139 107 .send(options.fields)
140 const req = request(options.url)
141 .put(options.path)
142 .set('Accept', 'application/json')
143 108
144 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 109 return buildRequest(req, { accept: 'application/json', expectedStatus: HttpStatusCode.BAD_REQUEST_400, ...options })
145
146 return req.send(options.fields)
147 .expect(options.statusCodeExpected)
148} 110}
149 111
150function makeHTMLRequest (url: string, path: string) { 112function decodeQueryString (path: string) {
151 return request(url) 113 return decode(path.split('?')[1])
152 .get(path)
153 .set('Accept', 'text/html')
154 .expect(HttpStatusCode.OK_200)
155} 114}
156 115
157function updateImageRequest (options: { 116function unwrapBody <T> (test: request.Test): Promise<T> {
158 url: string 117 return test.then(res => res.body)
159 path: string
160 accessToken: string
161 fixture: string
162 fieldname: string
163}) {
164 let filePath = ''
165 if (isAbsolute(options.fixture)) {
166 filePath = options.fixture
167 } else {
168 filePath = join(root(), 'server', 'tests', 'fixtures', options.fixture)
169 }
170
171 return makeUploadRequest({
172 url: options.url,
173 path: options.path,
174 token: options.accessToken,
175 fields: {},
176 attaches: { [options.fieldname]: filePath },
177 statusCodeExpected: HttpStatusCode.OK_200
178 })
179} 118}
180 119
181function decodeQueryString (path: string) { 120function unwrapText (test: request.Test): Promise<string> {
182 return decode(path.split('?')[1]) 121 return test.then(res => res.text)
183} 122}
184 123
185// --------------------------------------------------------------------------- 124// ---------------------------------------------------------------------------
186 125
187export { 126export {
188 get4KFileUrl,
189 makeHTMLRequest, 127 makeHTMLRequest,
190 makeGetRequest, 128 makeGetRequest,
191 decodeQueryString, 129 decodeQueryString,
@@ -194,5 +132,51 @@ export {
194 makePutBodyRequest, 132 makePutBodyRequest,
195 makeDeleteRequest, 133 makeDeleteRequest,
196 makeRawRequest, 134 makeRawRequest,
197 updateImageRequest 135 makeActivityPubGetRequest,
136 unwrapBody,
137 unwrapText
138}
139
140// ---------------------------------------------------------------------------
141
142function buildRequest (req: request.Test, options: CommonRequestParams) {
143 if (options.contentType) req.set('Accept', options.contentType)
144 if (options.token) req.set('Authorization', 'Bearer ' + options.token)
145 if (options.range) req.set('Range', options.range)
146 if (options.accept) req.set('Accept', options.accept)
147 if (options.host) req.set('Host', options.host)
148 if (options.redirects) req.redirects(options.redirects)
149 if (options.expectedStatus) req.expect(options.expectedStatus)
150 if (options.xForwardedFor) req.set('X-Forwarded-For', options.xForwardedFor)
151 if (options.type) req.type(options.type)
152
153 Object.keys(options.headers || {}).forEach(name => {
154 req.set(name, options.headers[name])
155 })
156
157 return req
158}
159
160function buildFields (req: request.Test, fields: { [ fieldName: string ]: any }, namespace?: string) {
161 if (!fields) return
162
163 let formKey: string
164
165 for (const key of Object.keys(fields)) {
166 if (namespace) formKey = `${namespace}[${key}]`
167 else formKey = key
168
169 if (fields[key] === undefined) continue
170
171 if (Array.isArray(fields[key]) && fields[key].length === 0) {
172 req.field(key, null)
173 continue
174 }
175
176 if (fields[key] !== null && typeof fields[key] === 'object') {
177 buildFields(req, fields[key], formKey)
178 } else {
179 req.field(formKey, fields[key])
180 }
181 }
198} 182}
diff --git a/shared/extra-utils/search/index.ts b/shared/extra-utils/search/index.ts
new file mode 100644
index 000000000..48dbe8ae9
--- /dev/null
+++ b/shared/extra-utils/search/index.ts
@@ -0,0 +1 @@
export * from './search-command'
diff --git a/shared/extra-utils/search/search-command.ts b/shared/extra-utils/search/search-command.ts
new file mode 100644
index 000000000..0fbbcd6ef
--- /dev/null
+++ b/shared/extra-utils/search/search-command.ts
@@ -0,0 +1,98 @@
1import {
2 HttpStatusCode,
3 ResultList,
4 Video,
5 VideoChannel,
6 VideoChannelsSearchQuery,
7 VideoPlaylist,
8 VideoPlaylistsSearchQuery,
9 VideosSearchQuery
10} from '@shared/models'
11import { AbstractCommand, OverrideCommandOptions } from '../shared'
12
13export class SearchCommand extends AbstractCommand {
14
15 searchChannels (options: OverrideCommandOptions & {
16 search: string
17 }) {
18 return this.advancedChannelSearch({
19 ...options,
20
21 search: { search: options.search }
22 })
23 }
24
25 advancedChannelSearch (options: OverrideCommandOptions & {
26 search: VideoChannelsSearchQuery
27 }) {
28 const { search } = options
29 const path = '/api/v1/search/video-channels'
30
31 return this.getRequestBody<ResultList<VideoChannel>>({
32 ...options,
33
34 path,
35 query: search,
36 implicitToken: false,
37 defaultExpectedStatus: HttpStatusCode.OK_200
38 })
39 }
40
41 searchPlaylists (options: OverrideCommandOptions & {
42 search: string
43 }) {
44 return this.advancedPlaylistSearch({
45 ...options,
46
47 search: { search: options.search }
48 })
49 }
50
51 advancedPlaylistSearch (options: OverrideCommandOptions & {
52 search: VideoPlaylistsSearchQuery
53 }) {
54 const { search } = options
55 const path = '/api/v1/search/video-playlists'
56
57 return this.getRequestBody<ResultList<VideoPlaylist>>({
58 ...options,
59
60 path,
61 query: search,
62 implicitToken: false,
63 defaultExpectedStatus: HttpStatusCode.OK_200
64 })
65 }
66
67 searchVideos (options: OverrideCommandOptions & {
68 search: string
69 sort?: string
70 }) {
71 const { search, sort } = options
72
73 return this.advancedVideoSearch({
74 ...options,
75
76 search: {
77 search: search,
78 sort: sort ?? '-publishedAt'
79 }
80 })
81 }
82
83 advancedVideoSearch (options: OverrideCommandOptions & {
84 search: VideosSearchQuery
85 }) {
86 const { search } = options
87 const path = '/api/v1/search/videos'
88
89 return this.getRequestBody<ResultList<Video>>({
90 ...options,
91
92 path,
93 query: search,
94 implicitToken: false,
95 defaultExpectedStatus: HttpStatusCode.OK_200
96 })
97 }
98}
diff --git a/shared/extra-utils/search/video-channels.ts b/shared/extra-utils/search/video-channels.ts
deleted file mode 100644
index 8e0f42578..000000000
--- a/shared/extra-utils/search/video-channels.ts
+++ /dev/null
@@ -1,36 +0,0 @@
1import { VideoChannelsSearchQuery } from '@shared/models'
2import { makeGetRequest } from '../requests/requests'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function searchVideoChannel (url: string, search: string, token?: string, statusCodeExpected = HttpStatusCode.OK_200) {
6 const path = '/api/v1/search/video-channels'
7
8 return makeGetRequest({
9 url,
10 path,
11 query: {
12 sort: '-createdAt',
13 search
14 },
15 token,
16 statusCodeExpected
17 })
18}
19
20function advancedVideoChannelSearch (url: string, search: VideoChannelsSearchQuery) {
21 const path = '/api/v1/search/video-channels'
22
23 return makeGetRequest({
24 url,
25 path,
26 query: search,
27 statusCodeExpected: HttpStatusCode.OK_200
28 })
29}
30
31// ---------------------------------------------------------------------------
32
33export {
34 searchVideoChannel,
35 advancedVideoChannelSearch
36}
diff --git a/shared/extra-utils/search/video-playlists.ts b/shared/extra-utils/search/video-playlists.ts
deleted file mode 100644
index c22831df7..000000000
--- a/shared/extra-utils/search/video-playlists.ts
+++ /dev/null
@@ -1,36 +0,0 @@
1import { VideoPlaylistsSearchQuery } from '@shared/models'
2import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes'
3import { makeGetRequest } from '../requests/requests'
4
5function searchVideoPlaylists (url: string, search: string, token?: string, statusCodeExpected = HttpStatusCode.OK_200) {
6 const path = '/api/v1/search/video-playlists'
7
8 return makeGetRequest({
9 url,
10 path,
11 query: {
12 sort: '-createdAt',
13 search
14 },
15 token,
16 statusCodeExpected
17 })
18}
19
20function advancedVideoPlaylistSearch (url: string, search: VideoPlaylistsSearchQuery) {
21 const path = '/api/v1/search/video-playlists'
22
23 return makeGetRequest({
24 url,
25 path,
26 query: search,
27 statusCodeExpected: HttpStatusCode.OK_200
28 })
29}
30
31// ---------------------------------------------------------------------------
32
33export {
34 searchVideoPlaylists,
35 advancedVideoPlaylistSearch
36}
diff --git a/shared/extra-utils/search/videos.ts b/shared/extra-utils/search/videos.ts
deleted file mode 100644
index db6edbd58..000000000
--- a/shared/extra-utils/search/videos.ts
+++ /dev/null
@@ -1,64 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import * as request from 'supertest'
4import { VideosSearchQuery } from '../../models/search'
5import { immutableAssign } from '../miscs/miscs'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
7
8function searchVideo (url: string, search: string, sort = '-publishedAt') {
9 const path = '/api/v1/search/videos'
10
11 const query = { sort, search: search }
12 const req = request(url)
13 .get(path)
14 .query(query)
15 .set('Accept', 'application/json')
16
17 return req.expect(HttpStatusCode.OK_200)
18 .expect('Content-Type', /json/)
19}
20
21function searchVideoWithToken (url: string, search: string, token: string, query: { nsfw?: boolean } = {}) {
22 const path = '/api/v1/search/videos'
23 const req = request(url)
24 .get(path)
25 .set('Authorization', 'Bearer ' + token)
26 .query(immutableAssign(query, { sort: '-publishedAt', search }))
27 .set('Accept', 'application/json')
28
29 return req.expect(HttpStatusCode.OK_200)
30 .expect('Content-Type', /json/)
31}
32
33function searchVideoWithSort (url: string, search: string, sort: string) {
34 const path = '/api/v1/search/videos'
35
36 const query = { search, sort }
37
38 return request(url)
39 .get(path)
40 .query(query)
41 .set('Accept', 'application/json')
42 .expect(HttpStatusCode.OK_200)
43 .expect('Content-Type', /json/)
44}
45
46function advancedVideosSearch (url: string, options: VideosSearchQuery) {
47 const path = '/api/v1/search/videos'
48
49 return request(url)
50 .get(path)
51 .query(options)
52 .set('Accept', 'application/json')
53 .expect(HttpStatusCode.OK_200)
54 .expect('Content-Type', /json/)
55}
56
57// ---------------------------------------------------------------------------
58
59export {
60 searchVideo,
61 advancedVideosSearch,
62 searchVideoWithToken,
63 searchVideoWithSort
64}
diff --git a/shared/extra-utils/server/activitypub.ts b/shared/extra-utils/server/activitypub.ts
deleted file mode 100644
index cf967ed7d..000000000
--- a/shared/extra-utils/server/activitypub.ts
+++ /dev/null
@@ -1,15 +0,0 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function makeActivityPubGetRequest (url: string, path: string, expectedStatus = HttpStatusCode.OK_200) {
5 return request(url)
6 .get(path)
7 .set('Accept', 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8')
8 .expect(expectedStatus)
9}
10
11// ---------------------------------------------------------------------------
12
13export {
14 makeActivityPubGetRequest
15}
diff --git a/shared/extra-utils/server/clients.ts b/shared/extra-utils/server/clients.ts
deleted file mode 100644
index 894fe4911..000000000
--- a/shared/extra-utils/server/clients.ts
+++ /dev/null
@@ -1,20 +0,0 @@
1import * as request from 'supertest'
2import { URL } from 'url'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function getClient (url: string) {
6 const path = '/api/v1/oauth-clients/local'
7
8 return request(url)
9 .get(path)
10 .set('Host', new URL(url).host)
11 .set('Accept', 'application/json')
12 .expect(HttpStatusCode.OK_200)
13 .expect('Content-Type', /json/)
14}
15
16// ---------------------------------------------------------------------------
17
18export {
19 getClient
20}
diff --git a/shared/extra-utils/server/config-command.ts b/shared/extra-utils/server/config-command.ts
new file mode 100644
index 000000000..11148aa46
--- /dev/null
+++ b/shared/extra-utils/server/config-command.ts
@@ -0,0 +1,263 @@
1import { merge } from 'lodash'
2import { DeepPartial } from '@shared/core-utils'
3import { About, HttpStatusCode, ServerConfig } from '@shared/models'
4import { CustomConfig } from '../../models/server/custom-config.model'
5import { AbstractCommand, OverrideCommandOptions } from '../shared'
6
7export class ConfigCommand extends AbstractCommand {
8
9 static getCustomConfigResolutions (enabled: boolean) {
10 return {
11 '240p': enabled,
12 '360p': enabled,
13 '480p': enabled,
14 '720p': enabled,
15 '1080p': enabled,
16 '1440p': enabled,
17 '2160p': enabled
18 }
19 }
20
21 getConfig (options: OverrideCommandOptions = {}) {
22 const path = '/api/v1/config'
23
24 return this.getRequestBody<ServerConfig>({
25 ...options,
26
27 path,
28 implicitToken: false,
29 defaultExpectedStatus: HttpStatusCode.OK_200
30 })
31 }
32
33 getAbout (options: OverrideCommandOptions = {}) {
34 const path = '/api/v1/config/about'
35
36 return this.getRequestBody<About>({
37 ...options,
38
39 path,
40 implicitToken: false,
41 defaultExpectedStatus: HttpStatusCode.OK_200
42 })
43 }
44
45 getCustomConfig (options: OverrideCommandOptions = {}) {
46 const path = '/api/v1/config/custom'
47
48 return this.getRequestBody<CustomConfig>({
49 ...options,
50
51 path,
52 implicitToken: true,
53 defaultExpectedStatus: HttpStatusCode.OK_200
54 })
55 }
56
57 updateCustomConfig (options: OverrideCommandOptions & {
58 newCustomConfig: CustomConfig
59 }) {
60 const path = '/api/v1/config/custom'
61
62 return this.putBodyRequest({
63 ...options,
64
65 path,
66 fields: options.newCustomConfig,
67 implicitToken: true,
68 defaultExpectedStatus: HttpStatusCode.OK_200
69 })
70 }
71
72 deleteCustomConfig (options: OverrideCommandOptions = {}) {
73 const path = '/api/v1/config/custom'
74
75 return this.deleteRequest({
76 ...options,
77
78 path,
79 implicitToken: true,
80 defaultExpectedStatus: HttpStatusCode.OK_200
81 })
82 }
83
84 updateCustomSubConfig (options: OverrideCommandOptions & {
85 newConfig: DeepPartial<CustomConfig>
86 }) {
87 const newCustomConfig: CustomConfig = {
88 instance: {
89 name: 'PeerTube updated',
90 shortDescription: 'my short description',
91 description: 'my super description',
92 terms: 'my super terms',
93 codeOfConduct: 'my super coc',
94
95 creationReason: 'my super creation reason',
96 moderationInformation: 'my super moderation information',
97 administrator: 'Kuja',
98 maintenanceLifetime: 'forever',
99 businessModel: 'my super business model',
100 hardwareInformation: '2vCore 3GB RAM',
101
102 languages: [ 'en', 'es' ],
103 categories: [ 1, 2 ],
104
105 isNSFW: true,
106 defaultNSFWPolicy: 'blur',
107
108 defaultClientRoute: '/videos/recently-added',
109
110 customizations: {
111 javascript: 'alert("coucou")',
112 css: 'body { background-color: red; }'
113 }
114 },
115 theme: {
116 default: 'default'
117 },
118 services: {
119 twitter: {
120 username: '@MySuperUsername',
121 whitelisted: true
122 }
123 },
124 cache: {
125 previews: {
126 size: 2
127 },
128 captions: {
129 size: 3
130 },
131 torrents: {
132 size: 4
133 }
134 },
135 signup: {
136 enabled: false,
137 limit: 5,
138 requiresEmailVerification: false,
139 minimumAge: 16
140 },
141 admin: {
142 email: 'superadmin1@example.com'
143 },
144 contactForm: {
145 enabled: true
146 },
147 user: {
148 videoQuota: 5242881,
149 videoQuotaDaily: 318742
150 },
151 transcoding: {
152 enabled: true,
153 allowAdditionalExtensions: true,
154 allowAudioFiles: true,
155 threads: 1,
156 concurrency: 3,
157 profile: 'default',
158 resolutions: {
159 '0p': false,
160 '240p': false,
161 '360p': true,
162 '480p': true,
163 '720p': false,
164 '1080p': false,
165 '1440p': false,
166 '2160p': false
167 },
168 webtorrent: {
169 enabled: true
170 },
171 hls: {
172 enabled: false
173 }
174 },
175 live: {
176 enabled: true,
177 allowReplay: false,
178 maxDuration: -1,
179 maxInstanceLives: -1,
180 maxUserLives: 50,
181 transcoding: {
182 enabled: true,
183 threads: 4,
184 profile: 'default',
185 resolutions: {
186 '240p': true,
187 '360p': true,
188 '480p': true,
189 '720p': true,
190 '1080p': true,
191 '1440p': true,
192 '2160p': true
193 }
194 }
195 },
196 import: {
197 videos: {
198 concurrency: 3,
199 http: {
200 enabled: false
201 },
202 torrent: {
203 enabled: false
204 }
205 }
206 },
207 trending: {
208 videos: {
209 algorithms: {
210 enabled: [ 'best', 'hot', 'most-viewed', 'most-liked' ],
211 default: 'hot'
212 }
213 }
214 },
215 autoBlacklist: {
216 videos: {
217 ofUsers: {
218 enabled: false
219 }
220 }
221 },
222 followers: {
223 instance: {
224 enabled: true,
225 manualApproval: false
226 }
227 },
228 followings: {
229 instance: {
230 autoFollowBack: {
231 enabled: false
232 },
233 autoFollowIndex: {
234 indexUrl: 'https://instances.joinpeertube.org/api/v1/instances/hosts',
235 enabled: false
236 }
237 }
238 },
239 broadcastMessage: {
240 enabled: true,
241 level: 'warning',
242 message: 'hello',
243 dismissable: true
244 },
245 search: {
246 remoteUri: {
247 users: true,
248 anonymous: true
249 },
250 searchIndex: {
251 enabled: true,
252 url: 'https://search.joinpeertube.org',
253 disableLocalSearch: true,
254 isDefaultSearch: true
255 }
256 }
257 }
258
259 merge(newCustomConfig, options.newConfig)
260
261 return this.updateCustomConfig({ ...options, newCustomConfig })
262 }
263}
diff --git a/shared/extra-utils/server/config.ts b/shared/extra-utils/server/config.ts
deleted file mode 100644
index 9fcfb31fd..000000000
--- a/shared/extra-utils/server/config.ts
+++ /dev/null
@@ -1,260 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makePutBodyRequest } from '../requests/requests'
2import { CustomConfig } from '../../models/server/custom-config.model'
3import { DeepPartial, HttpStatusCode } from '@shared/core-utils'
4import { merge } from 'lodash'
5
6function getConfig (url: string) {
7 const path = '/api/v1/config'
8
9 return makeGetRequest({
10 url,
11 path,
12 statusCodeExpected: HttpStatusCode.OK_200
13 })
14}
15
16function getAbout (url: string) {
17 const path = '/api/v1/config/about'
18
19 return makeGetRequest({
20 url,
21 path,
22 statusCodeExpected: HttpStatusCode.OK_200
23 })
24}
25
26function getCustomConfig (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
27 const path = '/api/v1/config/custom'
28
29 return makeGetRequest({
30 url,
31 token,
32 path,
33 statusCodeExpected
34 })
35}
36
37function updateCustomConfig (url: string, token: string, newCustomConfig: CustomConfig, statusCodeExpected = HttpStatusCode.OK_200) {
38 const path = '/api/v1/config/custom'
39
40 return makePutBodyRequest({
41 url,
42 token,
43 path,
44 fields: newCustomConfig,
45 statusCodeExpected
46 })
47}
48
49function updateCustomSubConfig (url: string, token: string, newConfig: DeepPartial<CustomConfig>) {
50 const updateParams: CustomConfig = {
51 instance: {
52 name: 'PeerTube updated',
53 shortDescription: 'my short description',
54 description: 'my super description',
55 terms: 'my super terms',
56 codeOfConduct: 'my super coc',
57
58 creationReason: 'my super creation reason',
59 moderationInformation: 'my super moderation information',
60 administrator: 'Kuja',
61 maintenanceLifetime: 'forever',
62 businessModel: 'my super business model',
63 hardwareInformation: '2vCore 3GB RAM',
64
65 languages: [ 'en', 'es' ],
66 categories: [ 1, 2 ],
67
68 isNSFW: true,
69 defaultNSFWPolicy: 'blur',
70
71 defaultClientRoute: '/videos/recently-added',
72
73 customizations: {
74 javascript: 'alert("coucou")',
75 css: 'body { background-color: red; }'
76 }
77 },
78 theme: {
79 default: 'default'
80 },
81 services: {
82 twitter: {
83 username: '@MySuperUsername',
84 whitelisted: true
85 }
86 },
87 cache: {
88 previews: {
89 size: 2
90 },
91 captions: {
92 size: 3
93 },
94 torrents: {
95 size: 4
96 }
97 },
98 signup: {
99 enabled: false,
100 limit: 5,
101 requiresEmailVerification: false,
102 minimumAge: 16
103 },
104 admin: {
105 email: 'superadmin1@example.com'
106 },
107 contactForm: {
108 enabled: true
109 },
110 user: {
111 videoQuota: 5242881,
112 videoQuotaDaily: 318742
113 },
114 transcoding: {
115 enabled: true,
116 allowAdditionalExtensions: true,
117 allowAudioFiles: true,
118 threads: 1,
119 concurrency: 3,
120 profile: 'default',
121 resolutions: {
122 '0p': false,
123 '240p': false,
124 '360p': true,
125 '480p': true,
126 '720p': false,
127 '1080p': false,
128 '1440p': false,
129 '2160p': false
130 },
131 webtorrent: {
132 enabled: true
133 },
134 hls: {
135 enabled: false
136 }
137 },
138 live: {
139 enabled: true,
140 allowReplay: false,
141 maxDuration: -1,
142 maxInstanceLives: -1,
143 maxUserLives: 50,
144 transcoding: {
145 enabled: true,
146 threads: 4,
147 profile: 'default',
148 resolutions: {
149 '240p': true,
150 '360p': true,
151 '480p': true,
152 '720p': true,
153 '1080p': true,
154 '1440p': true,
155 '2160p': true
156 }
157 }
158 },
159 import: {
160 videos: {
161 concurrency: 3,
162 http: {
163 enabled: false
164 },
165 torrent: {
166 enabled: false
167 }
168 }
169 },
170 trending: {
171 videos: {
172 algorithms: {
173 enabled: [ 'best', 'hot', 'most-viewed', 'most-liked' ],
174 default: 'hot'
175 }
176 }
177 },
178 autoBlacklist: {
179 videos: {
180 ofUsers: {
181 enabled: false
182 }
183 }
184 },
185 followers: {
186 instance: {
187 enabled: true,
188 manualApproval: false
189 }
190 },
191 followings: {
192 instance: {
193 autoFollowBack: {
194 enabled: false
195 },
196 autoFollowIndex: {
197 indexUrl: 'https://instances.joinpeertube.org/api/v1/instances/hosts',
198 enabled: false
199 }
200 }
201 },
202 broadcastMessage: {
203 enabled: true,
204 level: 'warning',
205 message: 'hello',
206 dismissable: true
207 },
208 search: {
209 remoteUri: {
210 users: true,
211 anonymous: true
212 },
213 searchIndex: {
214 enabled: true,
215 url: 'https://search.joinpeertube.org',
216 disableLocalSearch: true,
217 isDefaultSearch: true
218 }
219 }
220 }
221
222 merge(updateParams, newConfig)
223
224 return updateCustomConfig(url, token, updateParams)
225}
226
227function getCustomConfigResolutions (enabled: boolean) {
228 return {
229 '240p': enabled,
230 '360p': enabled,
231 '480p': enabled,
232 '720p': enabled,
233 '1080p': enabled,
234 '1440p': enabled,
235 '2160p': enabled
236 }
237}
238
239function deleteCustomConfig (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
240 const path = '/api/v1/config/custom'
241
242 return makeDeleteRequest({
243 url,
244 token,
245 path,
246 statusCodeExpected
247 })
248}
249
250// ---------------------------------------------------------------------------
251
252export {
253 getConfig,
254 getCustomConfig,
255 updateCustomConfig,
256 getAbout,
257 deleteCustomConfig,
258 updateCustomSubConfig,
259 getCustomConfigResolutions
260}
diff --git a/shared/extra-utils/server/contact-form-command.ts b/shared/extra-utils/server/contact-form-command.ts
new file mode 100644
index 000000000..0e8fd6d84
--- /dev/null
+++ b/shared/extra-utils/server/contact-form-command.ts
@@ -0,0 +1,31 @@
1import { HttpStatusCode } from '@shared/models'
2import { ContactForm } from '../../models/server'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class ContactFormCommand extends AbstractCommand {
6
7 send (options: OverrideCommandOptions & {
8 fromEmail: string
9 fromName: string
10 subject: string
11 body: string
12 }) {
13 const path = '/api/v1/server/contact'
14
15 const body: ContactForm = {
16 fromEmail: options.fromEmail,
17 fromName: options.fromName,
18 subject: options.subject,
19 body: options.body
20 }
21
22 return this.postBodyRequest({
23 ...options,
24
25 path,
26 fields: body,
27 implicitToken: false,
28 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
29 })
30 }
31}
diff --git a/shared/extra-utils/server/contact-form.ts b/shared/extra-utils/server/contact-form.ts
deleted file mode 100644
index 6c9232cc6..000000000
--- a/shared/extra-utils/server/contact-form.ts
+++ /dev/null
@@ -1,31 +0,0 @@
1import * as request from 'supertest'
2import { ContactForm } from '../../models/server'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function sendContactForm (options: {
6 url: string
7 fromEmail: string
8 fromName: string
9 subject: string
10 body: string
11 expectedStatus?: number
12}) {
13 const path = '/api/v1/server/contact'
14
15 const body: ContactForm = {
16 fromEmail: options.fromEmail,
17 fromName: options.fromName,
18 subject: options.subject,
19 body: options.body
20 }
21 return request(options.url)
22 .post(path)
23 .send(body)
24 .expect(options.expectedStatus || HttpStatusCode.NO_CONTENT_204)
25}
26
27// ---------------------------------------------------------------------------
28
29export {
30 sendContactForm
31}
diff --git a/shared/extra-utils/server/debug-command.ts b/shared/extra-utils/server/debug-command.ts
new file mode 100644
index 000000000..3c5a785bb
--- /dev/null
+++ b/shared/extra-utils/server/debug-command.ts
@@ -0,0 +1,33 @@
1import { Debug, HttpStatusCode, SendDebugCommand } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class DebugCommand extends AbstractCommand {
5
6 getDebug (options: OverrideCommandOptions = {}) {
7 const path = '/api/v1/server/debug'
8
9 return this.getRequestBody<Debug>({
10 ...options,
11
12 path,
13 implicitToken: true,
14 defaultExpectedStatus: HttpStatusCode.OK_200
15 })
16 }
17
18 sendCommand (options: OverrideCommandOptions & {
19 body: SendDebugCommand
20 }) {
21 const { body } = options
22 const path = '/api/v1/server/debug/run-command'
23
24 return this.postBodyRequest({
25 ...options,
26
27 path,
28 fields: body,
29 implicitToken: true,
30 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
31 })
32 }
33}
diff --git a/shared/extra-utils/server/debug.ts b/shared/extra-utils/server/debug.ts
deleted file mode 100644
index f196812b7..000000000
--- a/shared/extra-utils/server/debug.ts
+++ /dev/null
@@ -1,33 +0,0 @@
1import { makeGetRequest, makePostBodyRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes'
3import { SendDebugCommand } from '@shared/models'
4
5function getDebug (url: string, token: string) {
6 const path = '/api/v1/server/debug'
7
8 return makeGetRequest({
9 url,
10 path,
11 token,
12 statusCodeExpected: HttpStatusCode.OK_200
13 })
14}
15
16function sendDebugCommand (url: string, token: string, body: SendDebugCommand) {
17 const path = '/api/v1/server/debug/run-command'
18
19 return makePostBodyRequest({
20 url,
21 path,
22 token,
23 fields: body,
24 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
25 })
26}
27
28// ---------------------------------------------------------------------------
29
30export {
31 getDebug,
32 sendDebugCommand
33}
diff --git a/shared/extra-utils/server/directories.ts b/shared/extra-utils/server/directories.ts
new file mode 100644
index 000000000..b6465cbf4
--- /dev/null
+++ b/shared/extra-utils/server/directories.ts
@@ -0,0 +1,34 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { pathExists, readdir } from 'fs-extra'
5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { PeerTubeServer } from './server'
8
9async function checkTmpIsEmpty (server: PeerTubeServer) {
10 await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ])
11
12 if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) {
13 await checkDirectoryIsEmpty(server, 'tmp/hls')
14 }
15}
16
17async function checkDirectoryIsEmpty (server: PeerTubeServer, directory: string, exceptions: string[] = []) {
18 const testDirectory = 'test' + server.internalServerNumber
19
20 const directoryPath = join(root(), testDirectory, directory)
21
22 const directoryExists = await pathExists(directoryPath)
23 expect(directoryExists).to.be.true
24
25 const files = await readdir(directoryPath)
26 const filtered = files.filter(f => exceptions.includes(f) === false)
27
28 expect(filtered).to.have.lengthOf(0)
29}
30
31export {
32 checkTmpIsEmpty,
33 checkDirectoryIsEmpty
34}
diff --git a/shared/extra-utils/server/follows-command.ts b/shared/extra-utils/server/follows-command.ts
new file mode 100644
index 000000000..2b889cf66
--- /dev/null
+++ b/shared/extra-utils/server/follows-command.ts
@@ -0,0 +1,141 @@
1import { pick } from 'lodash'
2import { ActivityPubActorType, ActorFollow, FollowState, HttpStatusCode, ResultList, ServerFollowCreate } from '@shared/models'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4import { PeerTubeServer } from './server'
5
6export class FollowsCommand extends AbstractCommand {
7
8 getFollowers (options: OverrideCommandOptions & {
9 start: number
10 count: number
11 sort: string
12 search?: string
13 actorType?: ActivityPubActorType
14 state?: FollowState
15 }) {
16 const path = '/api/v1/server/followers'
17
18 const toPick = [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ]
19 const query = pick(options, toPick)
20
21 return this.getRequestBody<ResultList<ActorFollow>>({
22 ...options,
23
24 path,
25 query,
26 implicitToken: false,
27 defaultExpectedStatus: HttpStatusCode.OK_200
28 })
29 }
30
31 getFollowings (options: OverrideCommandOptions & {
32 start?: number
33 count?: number
34 sort?: string
35 search?: string
36 actorType?: ActivityPubActorType
37 state?: FollowState
38 } = {}) {
39 const path = '/api/v1/server/following'
40
41 const toPick = [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ]
42 const query = pick(options, toPick)
43
44 return this.getRequestBody<ResultList<ActorFollow>>({
45 ...options,
46
47 path,
48 query,
49 implicitToken: false,
50 defaultExpectedStatus: HttpStatusCode.OK_200
51 })
52 }
53
54 follow (options: OverrideCommandOptions & {
55 hosts?: string[]
56 handles?: string[]
57 }) {
58 const path = '/api/v1/server/following'
59
60 const fields: ServerFollowCreate = {}
61
62 if (options.hosts) {
63 fields.hosts = options.hosts.map(f => f.replace(/^http:\/\//, ''))
64 }
65
66 if (options.handles) {
67 fields.handles = options.handles
68 }
69
70 return this.postBodyRequest({
71 ...options,
72
73 path,
74 fields,
75 implicitToken: true,
76 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
77 })
78 }
79
80 async unfollow (options: OverrideCommandOptions & {
81 target: PeerTubeServer | string
82 }) {
83 const { target } = options
84
85 const handle = typeof target === 'string'
86 ? target
87 : target.host
88
89 const path = '/api/v1/server/following/' + handle
90
91 return this.deleteRequest({
92 ...options,
93
94 path,
95 implicitToken: true,
96 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
97 })
98 }
99
100 acceptFollower (options: OverrideCommandOptions & {
101 follower: string
102 }) {
103 const path = '/api/v1/server/followers/' + options.follower + '/accept'
104
105 return this.postBodyRequest({
106 ...options,
107
108 path,
109 implicitToken: true,
110 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
111 })
112 }
113
114 rejectFollower (options: OverrideCommandOptions & {
115 follower: string
116 }) {
117 const path = '/api/v1/server/followers/' + options.follower + '/reject'
118
119 return this.postBodyRequest({
120 ...options,
121
122 path,
123 implicitToken: true,
124 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
125 })
126 }
127
128 removeFollower (options: OverrideCommandOptions & {
129 follower: PeerTubeServer
130 }) {
131 const path = '/api/v1/server/followers/peertube@' + options.follower.host
132
133 return this.deleteRequest({
134 ...options,
135
136 path,
137 implicitToken: true,
138 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
139 })
140 }
141}
diff --git a/shared/extra-utils/server/follows.ts b/shared/extra-utils/server/follows.ts
index 6aae4a31d..698238f29 100644
--- a/shared/extra-utils/server/follows.ts
+++ b/shared/extra-utils/server/follows.ts
@@ -1,126 +1,10 @@
1import * as request from 'supertest'
2import { ServerInfo } from './servers'
3import { waitJobs } from './jobs' 1import { waitJobs } from './jobs'
4import { makePostBodyRequest } from '../requests/requests' 2import { PeerTubeServer } from './server'
5import { ActivityPubActorType, FollowState } from '@shared/models'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
7 3
8function getFollowersListPaginationAndSort (options: { 4async function doubleFollow (server1: PeerTubeServer, server2: PeerTubeServer) {
9 url: string
10 start: number
11 count: number
12 sort: string
13 search?: string
14 actorType?: ActivityPubActorType
15 state?: FollowState
16}) {
17 const { url, start, count, sort, search, state, actorType } = options
18 const path = '/api/v1/server/followers'
19
20 const query = {
21 start,
22 count,
23 sort,
24 search,
25 state,
26 actorType
27 }
28
29 return request(url)
30 .get(path)
31 .query(query)
32 .set('Accept', 'application/json')
33 .expect(HttpStatusCode.OK_200)
34 .expect('Content-Type', /json/)
35}
36
37function acceptFollower (url: string, token: string, follower: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
38 const path = '/api/v1/server/followers/' + follower + '/accept'
39
40 return makePostBodyRequest({
41 url,
42 token,
43 path,
44 statusCodeExpected
45 })
46}
47
48function rejectFollower (url: string, token: string, follower: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
49 const path = '/api/v1/server/followers/' + follower + '/reject'
50
51 return makePostBodyRequest({
52 url,
53 token,
54 path,
55 statusCodeExpected
56 })
57}
58
59function getFollowingListPaginationAndSort (options: {
60 url: string
61 start: number
62 count: number
63 sort: string
64 search?: string
65 actorType?: ActivityPubActorType
66 state?: FollowState
67}) {
68 const { url, start, count, sort, search, state, actorType } = options
69 const path = '/api/v1/server/following'
70
71 const query = {
72 start,
73 count,
74 sort,
75 search,
76 state,
77 actorType
78 }
79
80 return request(url)
81 .get(path)
82 .query(query)
83 .set('Accept', 'application/json')
84 .expect(HttpStatusCode.OK_200)
85 .expect('Content-Type', /json/)
86}
87
88function follow (follower: string, following: string[], accessToken: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
89 const path = '/api/v1/server/following'
90
91 const followingHosts = following.map(f => f.replace(/^http:\/\//, ''))
92 return request(follower)
93 .post(path)
94 .set('Accept', 'application/json')
95 .set('Authorization', 'Bearer ' + accessToken)
96 .send({ hosts: followingHosts })
97 .expect(expectedStatus)
98}
99
100async function unfollow (url: string, accessToken: string, target: ServerInfo, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
101 const path = '/api/v1/server/following/' + target.host
102
103 return request(url)
104 .delete(path)
105 .set('Accept', 'application/json')
106 .set('Authorization', 'Bearer ' + accessToken)
107 .expect(expectedStatus)
108}
109
110function removeFollower (url: string, accessToken: string, follower: ServerInfo, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
111 const path = '/api/v1/server/followers/peertube@' + follower.host
112
113 return request(url)
114 .delete(path)
115 .set('Accept', 'application/json')
116 .set('Authorization', 'Bearer ' + accessToken)
117 .expect(expectedStatus)
118}
119
120async function doubleFollow (server1: ServerInfo, server2: ServerInfo) {
121 await Promise.all([ 5 await Promise.all([
122 follow(server1.url, [ server2.url ], server1.accessToken), 6 server1.follows.follow({ hosts: [ server2.url ] }),
123 follow(server2.url, [ server1.url ], server2.accessToken) 7 server2.follows.follow({ hosts: [ server1.url ] })
124 ]) 8 ])
125 9
126 // Wait request propagation 10 // Wait request propagation
@@ -132,12 +16,5 @@ async function doubleFollow (server1: ServerInfo, server2: ServerInfo) {
132// --------------------------------------------------------------------------- 16// ---------------------------------------------------------------------------
133 17
134export { 18export {
135 getFollowersListPaginationAndSort, 19 doubleFollow
136 getFollowingListPaginationAndSort,
137 unfollow,
138 removeFollower,
139 follow,
140 doubleFollow,
141 acceptFollower,
142 rejectFollower
143} 20}
diff --git a/shared/extra-utils/server/index.ts b/shared/extra-utils/server/index.ts
new file mode 100644
index 000000000..9055dfc57
--- /dev/null
+++ b/shared/extra-utils/server/index.ts
@@ -0,0 +1,15 @@
1export * from './config-command'
2export * from './contact-form-command'
3export * from './debug-command'
4export * from './directories'
5export * from './follows-command'
6export * from './follows'
7export * from './jobs'
8export * from './jobs-command'
9export * from './plugins-command'
10export * from './plugins'
11export * from './redundancy-command'
12export * from './server'
13export * from './servers-command'
14export * from './servers'
15export * from './stats-command'
diff --git a/shared/extra-utils/server/jobs-command.ts b/shared/extra-utils/server/jobs-command.ts
new file mode 100644
index 000000000..09a299e5b
--- /dev/null
+++ b/shared/extra-utils/server/jobs-command.ts
@@ -0,0 +1,36 @@
1import { pick } from 'lodash'
2import { HttpStatusCode } from '@shared/models'
3import { Job, JobState, JobType, ResultList } from '../../models'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class JobsCommand extends AbstractCommand {
7
8 getJobsList (options: OverrideCommandOptions & {
9 state?: JobState
10 jobType?: JobType
11 start?: number
12 count?: number
13 sort?: string
14 } = {}) {
15 const path = this.buildJobsUrl(options.state)
16
17 const query = pick(options, [ 'start', 'count', 'sort', 'jobType' ])
18
19 return this.getRequestBody<ResultList<Job>>({
20 ...options,
21
22 path,
23 query,
24 implicitToken: true,
25 defaultExpectedStatus: HttpStatusCode.OK_200
26 })
27 }
28
29 private buildJobsUrl (state?: JobState) {
30 let path = '/api/v1/jobs'
31
32 if (state) path += '/' + state
33
34 return path
35 }
36}
diff --git a/shared/extra-utils/server/jobs.ts b/shared/extra-utils/server/jobs.ts
index 763374e03..64a0353eb 100644
--- a/shared/extra-utils/server/jobs.ts
+++ b/shared/extra-utils/server/jobs.ts
@@ -1,66 +1,17 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3import { getDebug, makeGetRequest } from '../../../shared/extra-utils'
4import { Job, JobState, JobType, ServerDebug } from '../../models'
5import { wait } from '../miscs/miscs'
6import { ServerInfo } from './servers'
7 1
8function buildJobsUrl (state?: JobState) { 2import { JobState } from '../../models'
9 let path = '/api/v1/jobs' 3import { wait } from '../miscs'
4import { PeerTubeServer } from './server'
10 5
11 if (state) path += '/' + state 6async function waitJobs (serversArg: PeerTubeServer[] | PeerTubeServer) {
12
13 return path
14}
15
16function getJobsList (url: string, accessToken: string, state?: JobState) {
17 const path = buildJobsUrl(state)
18
19 return request(url)
20 .get(path)
21 .set('Accept', 'application/json')
22 .set('Authorization', 'Bearer ' + accessToken)
23 .expect(HttpStatusCode.OK_200)
24 .expect('Content-Type', /json/)
25}
26
27function getJobsListPaginationAndSort (options: {
28 url: string
29 accessToken: string
30 start: number
31 count: number
32 sort: string
33 state?: JobState
34 jobType?: JobType
35}) {
36 const { url, accessToken, state, start, count, sort, jobType } = options
37 const path = buildJobsUrl(state)
38
39 const query = {
40 start,
41 count,
42 sort,
43 jobType
44 }
45
46 return makeGetRequest({
47 url,
48 path,
49 token: accessToken,
50 statusCodeExpected: HttpStatusCode.OK_200,
51 query
52 })
53}
54
55async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
56 const pendingJobWait = process.env.NODE_PENDING_JOB_WAIT 7 const pendingJobWait = process.env.NODE_PENDING_JOB_WAIT
57 ? parseInt(process.env.NODE_PENDING_JOB_WAIT, 10) 8 ? parseInt(process.env.NODE_PENDING_JOB_WAIT, 10)
58 : 250 9 : 250
59 10
60 let servers: ServerInfo[] 11 let servers: PeerTubeServer[]
61 12
62 if (Array.isArray(serversArg) === false) servers = [ serversArg as ServerInfo ] 13 if (Array.isArray(serversArg) === false) servers = [ serversArg as PeerTubeServer ]
63 else servers = serversArg as ServerInfo[] 14 else servers = serversArg as PeerTubeServer[]
64 15
65 const states: JobState[] = [ 'waiting', 'active', 'delayed' ] 16 const states: JobState[] = [ 'waiting', 'active', 'delayed' ]
66 const repeatableJobs = [ 'videos-views', 'activitypub-cleaner' ] 17 const repeatableJobs = [ 'videos-views', 'activitypub-cleaner' ]
@@ -72,15 +23,13 @@ async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
72 // Check if each server has pending request 23 // Check if each server has pending request
73 for (const server of servers) { 24 for (const server of servers) {
74 for (const state of states) { 25 for (const state of states) {
75 const p = getJobsListPaginationAndSort({ 26 const p = server.jobs.getJobsList({
76 url: server.url, 27 state,
77 accessToken: server.accessToken,
78 state: state,
79 start: 0, 28 start: 0,
80 count: 10, 29 count: 10,
81 sort: '-createdAt' 30 sort: '-createdAt'
82 }).then(res => res.body.data) 31 }).then(body => body.data)
83 .then((jobs: Job[]) => jobs.filter(j => !repeatableJobs.includes(j.type))) 32 .then(jobs => jobs.filter(j => !repeatableJobs.includes(j.type)))
84 .then(jobs => { 33 .then(jobs => {
85 if (jobs.length !== 0) { 34 if (jobs.length !== 0) {
86 pendingRequests = true 35 pendingRequests = true
@@ -90,9 +39,8 @@ async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
90 tasks.push(p) 39 tasks.push(p)
91 } 40 }
92 41
93 const p = getDebug(server.url, server.accessToken) 42 const p = server.debug.getDebug()
94 .then(res => res.body) 43 .then(obj => {
95 .then((obj: ServerDebug) => {
96 if (obj.activityPubMessagesWaiting !== 0) { 44 if (obj.activityPubMessagesWaiting !== 0) {
97 pendingRequests = true 45 pendingRequests = true
98 } 46 }
@@ -123,7 +71,5 @@ async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
123// --------------------------------------------------------------------------- 71// ---------------------------------------------------------------------------
124 72
125export { 73export {
126 getJobsList, 74 waitJobs
127 waitJobs,
128 getJobsListPaginationAndSort
129} 75}
diff --git a/shared/extra-utils/server/plugins-command.ts b/shared/extra-utils/server/plugins-command.ts
new file mode 100644
index 000000000..b944475a2
--- /dev/null
+++ b/shared/extra-utils/server/plugins-command.ts
@@ -0,0 +1,256 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { readJSON, writeJSON } from 'fs-extra'
4import { join } from 'path'
5import { root } from '@server/helpers/core-utils'
6import {
7 HttpStatusCode,
8 PeerTubePlugin,
9 PeerTubePluginIndex,
10 PeertubePluginIndexList,
11 PluginPackageJson,
12 PluginTranslation,
13 PluginType,
14 PublicServerSetting,
15 RegisteredServerSettings,
16 ResultList
17} from '@shared/models'
18import { AbstractCommand, OverrideCommandOptions } from '../shared'
19
20export class PluginsCommand extends AbstractCommand {
21
22 static getPluginTestPath (suffix = '') {
23 return join(root(), 'server', 'tests', 'fixtures', 'peertube-plugin-test' + suffix)
24 }
25
26 list (options: OverrideCommandOptions & {
27 start?: number
28 count?: number
29 sort?: string
30 pluginType?: PluginType
31 uninstalled?: boolean
32 }) {
33 const { start, count, sort, pluginType, uninstalled } = options
34 const path = '/api/v1/plugins'
35
36 return this.getRequestBody<ResultList<PeerTubePlugin>>({
37 ...options,
38
39 path,
40 query: {
41 start,
42 count,
43 sort,
44 pluginType,
45 uninstalled
46 },
47 implicitToken: true,
48 defaultExpectedStatus: HttpStatusCode.OK_200
49 })
50 }
51
52 listAvailable (options: OverrideCommandOptions & {
53 start?: number
54 count?: number
55 sort?: string
56 pluginType?: PluginType
57 currentPeerTubeEngine?: string
58 search?: string
59 expectedStatus?: HttpStatusCode
60 }) {
61 const { start, count, sort, pluginType, search, currentPeerTubeEngine } = options
62 const path = '/api/v1/plugins/available'
63
64 const query: PeertubePluginIndexList = {
65 start,
66 count,
67 sort,
68 pluginType,
69 currentPeerTubeEngine,
70 search
71 }
72
73 return this.getRequestBody<ResultList<PeerTubePluginIndex>>({
74 ...options,
75
76 path,
77 query,
78 implicitToken: true,
79 defaultExpectedStatus: HttpStatusCode.OK_200
80 })
81 }
82
83 get (options: OverrideCommandOptions & {
84 npmName: string
85 }) {
86 const path = '/api/v1/plugins/' + options.npmName
87
88 return this.getRequestBody<PeerTubePlugin>({
89 ...options,
90
91 path,
92 implicitToken: true,
93 defaultExpectedStatus: HttpStatusCode.OK_200
94 })
95 }
96
97 updateSettings (options: OverrideCommandOptions & {
98 npmName: string
99 settings: any
100 }) {
101 const { npmName, settings } = options
102 const path = '/api/v1/plugins/' + npmName + '/settings'
103
104 return this.putBodyRequest({
105 ...options,
106
107 path,
108 fields: { settings },
109 implicitToken: true,
110 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
111 })
112 }
113
114 getRegisteredSettings (options: OverrideCommandOptions & {
115 npmName: string
116 }) {
117 const path = '/api/v1/plugins/' + options.npmName + '/registered-settings'
118
119 return this.getRequestBody<RegisteredServerSettings>({
120 ...options,
121
122 path,
123 implicitToken: true,
124 defaultExpectedStatus: HttpStatusCode.OK_200
125 })
126 }
127
128 getPublicSettings (options: OverrideCommandOptions & {
129 npmName: string
130 }) {
131 const { npmName } = options
132 const path = '/api/v1/plugins/' + npmName + '/public-settings'
133
134 return this.getRequestBody<PublicServerSetting>({
135 ...options,
136
137 path,
138 implicitToken: false,
139 defaultExpectedStatus: HttpStatusCode.OK_200
140 })
141 }
142
143 getTranslations (options: OverrideCommandOptions & {
144 locale: string
145 }) {
146 const { locale } = options
147 const path = '/plugins/translations/' + locale + '.json'
148
149 return this.getRequestBody<PluginTranslation>({
150 ...options,
151
152 path,
153 implicitToken: false,
154 defaultExpectedStatus: HttpStatusCode.OK_200
155 })
156 }
157
158 install (options: OverrideCommandOptions & {
159 path?: string
160 npmName?: string
161 }) {
162 const { npmName, path } = options
163 const apiPath = '/api/v1/plugins/install'
164
165 return this.postBodyRequest({
166 ...options,
167
168 path: apiPath,
169 fields: { npmName, path },
170 implicitToken: true,
171 defaultExpectedStatus: HttpStatusCode.OK_200
172 })
173 }
174
175 update (options: OverrideCommandOptions & {
176 path?: string
177 npmName?: string
178 }) {
179 const { npmName, path } = options
180 const apiPath = '/api/v1/plugins/update'
181
182 return this.postBodyRequest({
183 ...options,
184
185 path: apiPath,
186 fields: { npmName, path },
187 implicitToken: true,
188 defaultExpectedStatus: HttpStatusCode.OK_200
189 })
190 }
191
192 uninstall (options: OverrideCommandOptions & {
193 npmName: string
194 }) {
195 const { npmName } = options
196 const apiPath = '/api/v1/plugins/uninstall'
197
198 return this.postBodyRequest({
199 ...options,
200
201 path: apiPath,
202 fields: { npmName },
203 implicitToken: true,
204 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
205 })
206 }
207
208 getCSS (options: OverrideCommandOptions = {}) {
209 const path = '/plugins/global.css'
210
211 return this.getRequestText({
212 ...options,
213
214 path,
215 implicitToken: false,
216 defaultExpectedStatus: HttpStatusCode.OK_200
217 })
218 }
219
220 getExternalAuth (options: OverrideCommandOptions & {
221 npmName: string
222 npmVersion: string
223 authName: string
224 query?: any
225 }) {
226 const { npmName, npmVersion, authName, query } = options
227
228 const path = '/plugins/' + npmName + '/' + npmVersion + '/auth/' + authName
229
230 return this.getRequest({
231 ...options,
232
233 path,
234 query,
235 implicitToken: false,
236 defaultExpectedStatus: HttpStatusCode.OK_200,
237 redirects: 0
238 })
239 }
240
241 updatePackageJSON (npmName: string, json: any) {
242 const path = this.getPackageJSONPath(npmName)
243
244 return writeJSON(path, json)
245 }
246
247 getPackageJSON (npmName: string): Promise<PluginPackageJson> {
248 const path = this.getPackageJSONPath(npmName)
249
250 return readJSON(path)
251 }
252
253 private getPackageJSONPath (npmName: string) {
254 return this.server.servers.buildDirectory(join('plugins', 'node_modules', npmName, 'package.json'))
255 }
256}
diff --git a/shared/extra-utils/server/plugins.ts b/shared/extra-utils/server/plugins.ts
index d53e5b382..0f5fabd5a 100644
--- a/shared/extra-utils/server/plugins.ts
+++ b/shared/extra-utils/server/plugins.ts
@@ -1,307 +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 { expect } from 'chai' 3import { expect } from 'chai'
4import { readJSON, writeJSON } from 'fs-extra' 4import { PeerTubeServer } from '../server/server'
5import { join } from 'path'
6import { RegisteredServerSettings } from '@shared/models'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
8import { PeertubePluginIndexList } from '../../models/plugins/plugin-index/peertube-plugin-index-list.model'
9import { PluginType } from '../../models/plugins/plugin.type'
10import { buildServerDirectory, root } from '../miscs/miscs'
11import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
12import { ServerInfo } from './servers'
13 5
14function listPlugins (parameters: { 6async function testHelloWorldRegisteredSettings (server: PeerTubeServer) {
15 url: string 7 const body = await server.plugins.getRegisteredSettings({ npmName: 'peertube-plugin-hello-world' })
16 accessToken: string
17 start?: number
18 count?: number
19 sort?: string
20 pluginType?: PluginType
21 uninstalled?: boolean
22 expectedStatus?: HttpStatusCode
23}) {
24 const { url, accessToken, start, count, sort, pluginType, uninstalled, expectedStatus = HttpStatusCode.OK_200 } = parameters
25 const path = '/api/v1/plugins'
26
27 return makeGetRequest({
28 url,
29 path,
30 token: accessToken,
31 query: {
32 start,
33 count,
34 sort,
35 pluginType,
36 uninstalled
37 },
38 statusCodeExpected: expectedStatus
39 })
40}
41
42function listAvailablePlugins (parameters: {
43 url: string
44 accessToken: string
45 start?: number
46 count?: number
47 sort?: string
48 pluginType?: PluginType
49 currentPeerTubeEngine?: string
50 search?: string
51 expectedStatus?: HttpStatusCode
52}) {
53 const {
54 url,
55 accessToken,
56 start,
57 count,
58 sort,
59 pluginType,
60 search,
61 currentPeerTubeEngine,
62 expectedStatus = HttpStatusCode.OK_200
63 } = parameters
64 const path = '/api/v1/plugins/available'
65
66 const query: PeertubePluginIndexList = {
67 start,
68 count,
69 sort,
70 pluginType,
71 currentPeerTubeEngine,
72 search
73 }
74
75 return makeGetRequest({
76 url,
77 path,
78 token: accessToken,
79 query,
80 statusCodeExpected: expectedStatus
81 })
82}
83
84function getPlugin (parameters: {
85 url: string
86 accessToken: string
87 npmName: string
88 expectedStatus?: HttpStatusCode
89}) {
90 const { url, accessToken, npmName, expectedStatus = HttpStatusCode.OK_200 } = parameters
91 const path = '/api/v1/plugins/' + npmName
92
93 return makeGetRequest({
94 url,
95 path,
96 token: accessToken,
97 statusCodeExpected: expectedStatus
98 })
99}
100
101function updatePluginSettings (parameters: {
102 url: string
103 accessToken: string
104 npmName: string
105 settings: any
106 expectedStatus?: HttpStatusCode
107}) {
108 const { url, accessToken, npmName, settings, expectedStatus = HttpStatusCode.NO_CONTENT_204 } = parameters
109 const path = '/api/v1/plugins/' + npmName + '/settings'
110
111 return makePutBodyRequest({
112 url,
113 path,
114 token: accessToken,
115 fields: { settings },
116 statusCodeExpected: expectedStatus
117 })
118}
119
120function getPluginRegisteredSettings (parameters: {
121 url: string
122 accessToken: string
123 npmName: string
124 expectedStatus?: HttpStatusCode
125}) {
126 const { url, accessToken, npmName, expectedStatus = HttpStatusCode.OK_200 } = parameters
127 const path = '/api/v1/plugins/' + npmName + '/registered-settings'
128
129 return makeGetRequest({
130 url,
131 path,
132 token: accessToken,
133 statusCodeExpected: expectedStatus
134 })
135}
136
137async function testHelloWorldRegisteredSettings (server: ServerInfo) {
138 const res = await getPluginRegisteredSettings({
139 url: server.url,
140 accessToken: server.accessToken,
141 npmName: 'peertube-plugin-hello-world'
142 })
143
144 const registeredSettings = (res.body as RegisteredServerSettings).registeredSettings
145 8
9 const registeredSettings = body.registeredSettings
146 expect(registeredSettings).to.have.length.at.least(1) 10 expect(registeredSettings).to.have.length.at.least(1)
147 11
148 const adminNameSettings = registeredSettings.find(s => s.name === 'admin-name') 12 const adminNameSettings = registeredSettings.find(s => s.name === 'admin-name')
149 expect(adminNameSettings).to.not.be.undefined 13 expect(adminNameSettings).to.not.be.undefined
150} 14}
151 15
152function getPublicSettings (parameters: {
153 url: string
154 npmName: string
155 expectedStatus?: HttpStatusCode
156}) {
157 const { url, npmName, expectedStatus = HttpStatusCode.OK_200 } = parameters
158 const path = '/api/v1/plugins/' + npmName + '/public-settings'
159
160 return makeGetRequest({
161 url,
162 path,
163 statusCodeExpected: expectedStatus
164 })
165}
166
167function getPluginTranslations (parameters: {
168 url: string
169 locale: string
170 expectedStatus?: HttpStatusCode
171}) {
172 const { url, locale, expectedStatus = HttpStatusCode.OK_200 } = parameters
173 const path = '/plugins/translations/' + locale + '.json'
174
175 return makeGetRequest({
176 url,
177 path,
178 statusCodeExpected: expectedStatus
179 })
180}
181
182function installPlugin (parameters: {
183 url: string
184 accessToken: string
185 path?: string
186 npmName?: string
187 expectedStatus?: HttpStatusCode
188}) {
189 const { url, accessToken, npmName, path, expectedStatus = HttpStatusCode.OK_200 } = parameters
190 const apiPath = '/api/v1/plugins/install'
191
192 return makePostBodyRequest({
193 url,
194 path: apiPath,
195 token: accessToken,
196 fields: { npmName, path },
197 statusCodeExpected: expectedStatus
198 })
199}
200
201function updatePlugin (parameters: {
202 url: string
203 accessToken: string
204 path?: string
205 npmName?: string
206 expectedStatus?: HttpStatusCode
207}) {
208 const { url, accessToken, npmName, path, expectedStatus = HttpStatusCode.OK_200 } = parameters
209 const apiPath = '/api/v1/plugins/update'
210
211 return makePostBodyRequest({
212 url,
213 path: apiPath,
214 token: accessToken,
215 fields: { npmName, path },
216 statusCodeExpected: expectedStatus
217 })
218}
219
220function uninstallPlugin (parameters: {
221 url: string
222 accessToken: string
223 npmName: string
224 expectedStatus?: HttpStatusCode
225}) {
226 const { url, accessToken, npmName, expectedStatus = HttpStatusCode.NO_CONTENT_204 } = parameters
227 const apiPath = '/api/v1/plugins/uninstall'
228
229 return makePostBodyRequest({
230 url,
231 path: apiPath,
232 token: accessToken,
233 fields: { npmName },
234 statusCodeExpected: expectedStatus
235 })
236}
237
238function getPluginsCSS (url: string) {
239 const path = '/plugins/global.css'
240
241 return makeGetRequest({
242 url,
243 path,
244 statusCodeExpected: HttpStatusCode.OK_200
245 })
246}
247
248function getPackageJSONPath (server: ServerInfo, npmName: string) {
249 return buildServerDirectory(server, join('plugins', 'node_modules', npmName, 'package.json'))
250}
251
252function updatePluginPackageJSON (server: ServerInfo, npmName: string, json: any) {
253 const path = getPackageJSONPath(server, npmName)
254
255 return writeJSON(path, json)
256}
257
258function getPluginPackageJSON (server: ServerInfo, npmName: string) {
259 const path = getPackageJSONPath(server, npmName)
260
261 return readJSON(path)
262}
263
264function getPluginTestPath (suffix = '') {
265 return join(root(), 'server', 'tests', 'fixtures', 'peertube-plugin-test' + suffix)
266}
267
268function getExternalAuth (options: {
269 url: string
270 npmName: string
271 npmVersion: string
272 authName: string
273 query?: any
274 statusCodeExpected?: HttpStatusCode
275}) {
276 const { url, npmName, npmVersion, authName, statusCodeExpected, query } = options
277
278 const path = '/plugins/' + npmName + '/' + npmVersion + '/auth/' + authName
279
280 return makeGetRequest({
281 url,
282 path,
283 query,
284 statusCodeExpected: statusCodeExpected || HttpStatusCode.OK_200,
285 redirects: 0
286 })
287}
288
289export { 16export {
290 listPlugins, 17 testHelloWorldRegisteredSettings
291 listAvailablePlugins,
292 installPlugin,
293 getPluginTranslations,
294 getPluginsCSS,
295 updatePlugin,
296 getPlugin,
297 uninstallPlugin,
298 testHelloWorldRegisteredSettings,
299 updatePluginSettings,
300 getPluginRegisteredSettings,
301 getPackageJSONPath,
302 updatePluginPackageJSON,
303 getPluginPackageJSON,
304 getPluginTestPath,
305 getPublicSettings,
306 getExternalAuth
307} 18}
diff --git a/shared/extra-utils/server/redundancy-command.ts b/shared/extra-utils/server/redundancy-command.ts
new file mode 100644
index 000000000..e7a8b3c29
--- /dev/null
+++ b/shared/extra-utils/server/redundancy-command.ts
@@ -0,0 +1,80 @@
1import { HttpStatusCode, ResultList, VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class RedundancyCommand extends AbstractCommand {
5
6 updateRedundancy (options: OverrideCommandOptions & {
7 host: string
8 redundancyAllowed: boolean
9 }) {
10 const { host, redundancyAllowed } = options
11 const path = '/api/v1/server/redundancy/' + host
12
13 return this.putBodyRequest({
14 ...options,
15
16 path,
17 fields: { redundancyAllowed },
18 implicitToken: true,
19 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
20 })
21 }
22
23 listVideos (options: OverrideCommandOptions & {
24 target: VideoRedundanciesTarget
25 start?: number
26 count?: number
27 sort?: string
28 }) {
29 const path = '/api/v1/server/redundancy/videos'
30
31 const { target, start, count, sort } = options
32
33 return this.getRequestBody<ResultList<VideoRedundancy>>({
34 ...options,
35
36 path,
37
38 query: {
39 start: start ?? 0,
40 count: count ?? 5,
41 sort: sort ?? 'name',
42 target
43 },
44
45 implicitToken: true,
46 defaultExpectedStatus: HttpStatusCode.OK_200
47 })
48 }
49
50 addVideo (options: OverrideCommandOptions & {
51 videoId: number
52 }) {
53 const path = '/api/v1/server/redundancy/videos'
54 const { videoId } = options
55
56 return this.postBodyRequest({
57 ...options,
58
59 path,
60 fields: { videoId },
61 implicitToken: true,
62 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
63 })
64 }
65
66 removeVideo (options: OverrideCommandOptions & {
67 redundancyId: number
68 }) {
69 const { redundancyId } = options
70 const path = '/api/v1/server/redundancy/videos/' + redundancyId
71
72 return this.deleteRequest({
73 ...options,
74
75 path,
76 implicitToken: true,
77 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
78 })
79 }
80}
diff --git a/shared/extra-utils/server/redundancy.ts b/shared/extra-utils/server/redundancy.ts
deleted file mode 100644
index b83815a37..000000000
--- a/shared/extra-utils/server/redundancy.ts
+++ /dev/null
@@ -1,88 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
2import { VideoRedundanciesTarget } from '@shared/models'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4
5function updateRedundancy (
6 url: string,
7 accessToken: string,
8 host: string,
9 redundancyAllowed: boolean,
10 expectedStatus = HttpStatusCode.NO_CONTENT_204
11) {
12 const path = '/api/v1/server/redundancy/' + host
13
14 return makePutBodyRequest({
15 url,
16 path,
17 token: accessToken,
18 fields: { redundancyAllowed },
19 statusCodeExpected: expectedStatus
20 })
21}
22
23function listVideoRedundancies (options: {
24 url: string
25 accessToken: string
26 target: VideoRedundanciesTarget
27 start?: number
28 count?: number
29 sort?: string
30 statusCodeExpected?: HttpStatusCode
31}) {
32 const path = '/api/v1/server/redundancy/videos'
33
34 const { url, accessToken, target, statusCodeExpected, start, count, sort } = options
35
36 return makeGetRequest({
37 url,
38 token: accessToken,
39 path,
40 query: {
41 start: start ?? 0,
42 count: count ?? 5,
43 sort: sort ?? 'name',
44 target
45 },
46 statusCodeExpected: statusCodeExpected || HttpStatusCode.OK_200
47 })
48}
49
50function addVideoRedundancy (options: {
51 url: string
52 accessToken: string
53 videoId: number
54}) {
55 const path = '/api/v1/server/redundancy/videos'
56 const { url, accessToken, videoId } = options
57
58 return makePostBodyRequest({
59 url,
60 token: accessToken,
61 path,
62 fields: { videoId },
63 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
64 })
65}
66
67function removeVideoRedundancy (options: {
68 url: string
69 accessToken: string
70 redundancyId: number
71}) {
72 const { url, accessToken, redundancyId } = options
73 const path = '/api/v1/server/redundancy/videos/' + redundancyId
74
75 return makeDeleteRequest({
76 url,
77 token: accessToken,
78 path,
79 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
80 })
81}
82
83export {
84 updateRedundancy,
85 listVideoRedundancies,
86 addVideoRedundancy,
87 removeVideoRedundancy
88}
diff --git a/shared/extra-utils/server/server.ts b/shared/extra-utils/server/server.ts
new file mode 100644
index 000000000..b33bb9d1e
--- /dev/null
+++ b/shared/extra-utils/server/server.ts
@@ -0,0 +1,378 @@
1import { ChildProcess, fork } from 'child_process'
2import { copy } from 'fs-extra'
3import { join } from 'path'
4import { root } from '@server/helpers/core-utils'
5import { randomInt } from '../../core-utils/miscs/miscs'
6import { VideoChannel } from '../../models/videos'
7import { BulkCommand } from '../bulk'
8import { CLICommand } from '../cli'
9import { CustomPagesCommand } from '../custom-pages'
10import { FeedCommand } from '../feeds'
11import { LogsCommand } from '../logs'
12import { parallelTests, SQLCommand } from '../miscs'
13import { AbusesCommand } from '../moderation'
14import { OverviewsCommand } from '../overviews'
15import { SearchCommand } from '../search'
16import { SocketIOCommand } from '../socket'
17import { AccountsCommand, BlocklistCommand, LoginCommand, NotificationsCommand, SubscriptionsCommand, UsersCommand } from '../users'
18import {
19 BlacklistCommand,
20 CaptionsCommand,
21 ChangeOwnershipCommand,
22 ChannelsCommand,
23 HistoryCommand,
24 ImportsCommand,
25 LiveCommand,
26 PlaylistsCommand,
27 ServicesCommand,
28 StreamingPlaylistsCommand,
29 VideosCommand
30} from '../videos'
31import { CommentsCommand } from '../videos/comments-command'
32import { ConfigCommand } from './config-command'
33import { ContactFormCommand } from './contact-form-command'
34import { DebugCommand } from './debug-command'
35import { FollowsCommand } from './follows-command'
36import { JobsCommand } from './jobs-command'
37import { PluginsCommand } from './plugins-command'
38import { RedundancyCommand } from './redundancy-command'
39import { ServersCommand } from './servers-command'
40import { StatsCommand } from './stats-command'
41
42export type RunServerOptions = {
43 hideLogs?: boolean
44 execArgv?: string[]
45}
46
47export class PeerTubeServer {
48 app?: ChildProcess
49
50 url: string
51 host?: string
52 hostname?: string
53 port?: number
54
55 rtmpPort?: number
56
57 parallel?: boolean
58 internalServerNumber: number
59
60 serverNumber?: number
61 customConfigFile?: string
62
63 store?: {
64 client?: {
65 id?: string
66 secret?: string
67 }
68
69 user?: {
70 username: string
71 password: string
72 email?: string
73 }
74
75 channel?: VideoChannel
76
77 video?: {
78 id: number
79 uuid: string
80 shortUUID: string
81 name?: string
82 url?: string
83
84 account?: {
85 name: string
86 }
87
88 embedPath?: string
89 }
90
91 videos?: { id: number, uuid: string }[]
92 }
93
94 accessToken?: string
95 refreshToken?: string
96
97 bulk?: BulkCommand
98 cli?: CLICommand
99 customPage?: CustomPagesCommand
100 feed?: FeedCommand
101 logs?: LogsCommand
102 abuses?: AbusesCommand
103 overviews?: OverviewsCommand
104 search?: SearchCommand
105 contactForm?: ContactFormCommand
106 debug?: DebugCommand
107 follows?: FollowsCommand
108 jobs?: JobsCommand
109 plugins?: PluginsCommand
110 redundancy?: RedundancyCommand
111 stats?: StatsCommand
112 config?: ConfigCommand
113 socketIO?: SocketIOCommand
114 accounts?: AccountsCommand
115 blocklist?: BlocklistCommand
116 subscriptions?: SubscriptionsCommand
117 live?: LiveCommand
118 services?: ServicesCommand
119 blacklist?: BlacklistCommand
120 captions?: CaptionsCommand
121 changeOwnership?: ChangeOwnershipCommand
122 playlists?: PlaylistsCommand
123 history?: HistoryCommand
124 imports?: ImportsCommand
125 streamingPlaylists?: StreamingPlaylistsCommand
126 channels?: ChannelsCommand
127 comments?: CommentsCommand
128 sql?: SQLCommand
129 notifications?: NotificationsCommand
130 servers?: ServersCommand
131 login?: LoginCommand
132 users?: UsersCommand
133 videos?: VideosCommand
134
135 constructor (options: { serverNumber: number } | { url: string }) {
136 if ((options as any).url) {
137 this.setUrl((options as any).url)
138 } else {
139 this.setServerNumber((options as any).serverNumber)
140 }
141
142 this.store = {
143 client: {
144 id: null,
145 secret: null
146 },
147 user: {
148 username: null,
149 password: null
150 }
151 }
152
153 this.assignCommands()
154 }
155
156 setServerNumber (serverNumber: number) {
157 this.serverNumber = serverNumber
158
159 this.parallel = parallelTests()
160
161 this.internalServerNumber = this.parallel ? this.randomServer() : this.serverNumber
162 this.rtmpPort = this.parallel ? this.randomRTMP() : 1936
163 this.port = 9000 + this.internalServerNumber
164
165 this.url = `http://localhost:${this.port}`
166 this.host = `localhost:${this.port}`
167 this.hostname = 'localhost'
168 }
169
170 setUrl (url: string) {
171 const parsed = new URL(url)
172
173 this.url = url
174 this.host = parsed.host
175 this.hostname = parsed.hostname
176 this.port = parseInt(parsed.port)
177 }
178
179 async flushAndRun (configOverride?: Object, args = [], options: RunServerOptions = {}) {
180 await ServersCommand.flushTests(this.internalServerNumber)
181
182 return this.run(configOverride, args, options)
183 }
184
185 async run (configOverrideArg?: any, args = [], options: RunServerOptions = {}) {
186 // These actions are async so we need to be sure that they have both been done
187 const serverRunString = {
188 'HTTP server listening': false
189 }
190 const key = 'Database peertube_test' + this.internalServerNumber + ' is ready'
191 serverRunString[key] = false
192
193 const regexps = {
194 client_id: 'Client id: (.+)',
195 client_secret: 'Client secret: (.+)',
196 user_username: 'Username: (.+)',
197 user_password: 'User password: (.+)'
198 }
199
200 await this.assignCustomConfigFile()
201
202 const configOverride = this.buildConfigOverride()
203
204 if (configOverrideArg !== undefined) {
205 Object.assign(configOverride, configOverrideArg)
206 }
207
208 // Share the environment
209 const env = Object.create(process.env)
210 env['NODE_ENV'] = 'test'
211 env['NODE_APP_INSTANCE'] = this.internalServerNumber.toString()
212 env['NODE_CONFIG'] = JSON.stringify(configOverride)
213
214 const forkOptions = {
215 silent: true,
216 env,
217 detached: true,
218 execArgv: options.execArgv || []
219 }
220
221 return new Promise<void>(res => {
222 const self = this
223
224 this.app = fork(join(root(), 'dist', 'server.js'), args, forkOptions)
225 this.app.stdout.on('data', function onStdout (data) {
226 let dontContinue = false
227
228 // Capture things if we want to
229 for (const key of Object.keys(regexps)) {
230 const regexp = regexps[key]
231 const matches = data.toString().match(regexp)
232 if (matches !== null) {
233 if (key === 'client_id') self.store.client.id = matches[1]
234 else if (key === 'client_secret') self.store.client.secret = matches[1]
235 else if (key === 'user_username') self.store.user.username = matches[1]
236 else if (key === 'user_password') self.store.user.password = matches[1]
237 }
238 }
239
240 // Check if all required sentences are here
241 for (const key of Object.keys(serverRunString)) {
242 if (data.toString().indexOf(key) !== -1) serverRunString[key] = true
243 if (serverRunString[key] === false) dontContinue = true
244 }
245
246 // If no, there is maybe one thing not already initialized (client/user credentials generation...)
247 if (dontContinue === true) return
248
249 if (options.hideLogs === false) {
250 console.log(data.toString())
251 } else {
252 self.app.stdout.removeListener('data', onStdout)
253 }
254
255 process.on('exit', () => {
256 try {
257 process.kill(self.app.pid)
258 } catch { /* empty */ }
259 })
260
261 res()
262 })
263 })
264 }
265
266 async kill () {
267 if (!this.app) return
268
269 await this.sql.cleanup()
270
271 process.kill(-this.app.pid)
272
273 this.app = null
274 }
275
276 private randomServer () {
277 const low = 10
278 const high = 10000
279
280 return randomInt(low, high)
281 }
282
283 private randomRTMP () {
284 const low = 1900
285 const high = 2100
286
287 return randomInt(low, high)
288 }
289
290 private async assignCustomConfigFile () {
291 if (this.internalServerNumber === this.serverNumber) return
292
293 const basePath = join(root(), 'config')
294
295 const tmpConfigFile = join(basePath, `test-${this.internalServerNumber}.yaml`)
296 await copy(join(basePath, `test-${this.serverNumber}.yaml`), tmpConfigFile)
297
298 this.customConfigFile = tmpConfigFile
299 }
300
301 private buildConfigOverride () {
302 if (!this.parallel) return {}
303
304 return {
305 listen: {
306 port: this.port
307 },
308 webserver: {
309 port: this.port
310 },
311 database: {
312 suffix: '_test' + this.internalServerNumber
313 },
314 storage: {
315 tmp: `test${this.internalServerNumber}/tmp/`,
316 avatars: `test${this.internalServerNumber}/avatars/`,
317 videos: `test${this.internalServerNumber}/videos/`,
318 streaming_playlists: `test${this.internalServerNumber}/streaming-playlists/`,
319 redundancy: `test${this.internalServerNumber}/redundancy/`,
320 logs: `test${this.internalServerNumber}/logs/`,
321 previews: `test${this.internalServerNumber}/previews/`,
322 thumbnails: `test${this.internalServerNumber}/thumbnails/`,
323 torrents: `test${this.internalServerNumber}/torrents/`,
324 captions: `test${this.internalServerNumber}/captions/`,
325 cache: `test${this.internalServerNumber}/cache/`,
326 plugins: `test${this.internalServerNumber}/plugins/`
327 },
328 admin: {
329 email: `admin${this.internalServerNumber}@example.com`
330 },
331 live: {
332 rtmp: {
333 port: this.rtmpPort
334 }
335 }
336 }
337 }
338
339 private assignCommands () {
340 this.bulk = new BulkCommand(this)
341 this.cli = new CLICommand(this)
342 this.customPage = new CustomPagesCommand(this)
343 this.feed = new FeedCommand(this)
344 this.logs = new LogsCommand(this)
345 this.abuses = new AbusesCommand(this)
346 this.overviews = new OverviewsCommand(this)
347 this.search = new SearchCommand(this)
348 this.contactForm = new ContactFormCommand(this)
349 this.debug = new DebugCommand(this)
350 this.follows = new FollowsCommand(this)
351 this.jobs = new JobsCommand(this)
352 this.plugins = new PluginsCommand(this)
353 this.redundancy = new RedundancyCommand(this)
354 this.stats = new StatsCommand(this)
355 this.config = new ConfigCommand(this)
356 this.socketIO = new SocketIOCommand(this)
357 this.accounts = new AccountsCommand(this)
358 this.blocklist = new BlocklistCommand(this)
359 this.subscriptions = new SubscriptionsCommand(this)
360 this.live = new LiveCommand(this)
361 this.services = new ServicesCommand(this)
362 this.blacklist = new BlacklistCommand(this)
363 this.captions = new CaptionsCommand(this)
364 this.changeOwnership = new ChangeOwnershipCommand(this)
365 this.playlists = new PlaylistsCommand(this)
366 this.history = new HistoryCommand(this)
367 this.imports = new ImportsCommand(this)
368 this.streamingPlaylists = new StreamingPlaylistsCommand(this)
369 this.channels = new ChannelsCommand(this)
370 this.comments = new CommentsCommand(this)
371 this.sql = new SQLCommand(this)
372 this.notifications = new NotificationsCommand(this)
373 this.servers = new ServersCommand(this)
374 this.login = new LoginCommand(this)
375 this.users = new UsersCommand(this)
376 this.videos = new VideosCommand(this)
377 }
378}
diff --git a/shared/extra-utils/server/servers-command.ts b/shared/extra-utils/server/servers-command.ts
new file mode 100644
index 000000000..107e2b4ad
--- /dev/null
+++ b/shared/extra-utils/server/servers-command.ts
@@ -0,0 +1,81 @@
1import { exec } from 'child_process'
2import { copy, ensureDir, readFile, remove } from 'fs-extra'
3import { join } from 'path'
4import { root } from '@server/helpers/core-utils'
5import { HttpStatusCode } from '@shared/models'
6import { getFileSize } from '@uploadx/core'
7import { isGithubCI, wait } from '../miscs'
8import { AbstractCommand, OverrideCommandOptions } from '../shared'
9
10export class ServersCommand extends AbstractCommand {
11
12 static flushTests (internalServerNumber: number) {
13 return new Promise<void>((res, rej) => {
14 const suffix = ` -- ${internalServerNumber}`
15
16 return exec('npm run clean:server:test' + suffix, (err, _stdout, stderr) => {
17 if (err || stderr) return rej(err || new Error(stderr))
18
19 return res()
20 })
21 })
22 }
23
24 ping (options: OverrideCommandOptions = {}) {
25 return this.getRequestBody({
26 ...options,
27
28 path: '/api/v1/ping',
29 implicitToken: false,
30 defaultExpectedStatus: HttpStatusCode.OK_200
31 })
32 }
33
34 async cleanupTests () {
35 const p: Promise<any>[] = []
36
37 if (isGithubCI()) {
38 await ensureDir('artifacts')
39
40 const origin = this.buildDirectory('logs/peertube.log')
41 const destname = `peertube-${this.server.internalServerNumber}.log`
42 console.log('Saving logs %s.', destname)
43
44 await copy(origin, join('artifacts', destname))
45 }
46
47 if (this.server.parallel) {
48 p.push(ServersCommand.flushTests(this.server.internalServerNumber))
49 }
50
51 if (this.server.customConfigFile) {
52 p.push(remove(this.server.customConfigFile))
53 }
54
55 return p
56 }
57
58 async waitUntilLog (str: string, count = 1, strictCount = true) {
59 const logfile = this.server.servers.buildDirectory('logs/peertube.log')
60
61 while (true) {
62 const buf = await readFile(logfile)
63
64 const matches = buf.toString().match(new RegExp(str, 'g'))
65 if (matches && matches.length === count) return
66 if (matches && strictCount === false && matches.length >= count) return
67
68 await wait(1000)
69 }
70 }
71
72 buildDirectory (directory: string) {
73 return join(root(), 'test' + this.server.internalServerNumber, directory)
74 }
75
76 async getServerFileSize (subPath: string) {
77 const path = this.server.servers.buildDirectory(subPath)
78
79 return getFileSize(path)
80 }
81}
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts
index 28e431e94..87d7e9449 100644
--- a/shared/extra-utils/server/servers.ts
+++ b/shared/extra-utils/server/servers.ts
@@ -1,384 +1,49 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ 1import { ensureDir } from 'fs-extra'
2import { isGithubCI } from '../miscs'
3import { PeerTubeServer, RunServerOptions } from './server'
2 4
3import { expect } from 'chai' 5async function createSingleServer (serverNumber: number, configOverride?: Object, args = [], options: RunServerOptions = {}) {
4import { ChildProcess, exec, fork } from 'child_process' 6 const server = new PeerTubeServer({ serverNumber })
5import { copy, ensureDir, pathExists, readdir, readFile, remove } from 'fs-extra'
6import { join } from 'path'
7import { randomInt } from '../../core-utils/miscs/miscs'
8import { VideoChannel } from '../../models/videos'
9import { buildServerDirectory, getFileSize, isGithubCI, root, wait } from '../miscs/miscs'
10import { makeGetRequest } from '../requests/requests'
11 7
12interface ServerInfo { 8 await server.flushAndRun(configOverride, args, options)
13 app: ChildProcess
14
15 url: string
16 host: string
17 hostname: string
18 port: number
19
20 rtmpPort: number
21
22 parallel: boolean
23 internalServerNumber: number
24 serverNumber: number
25
26 client: {
27 id: string
28 secret: string
29 }
30
31 user: {
32 username: string
33 password: string
34 email?: string
35 }
36
37 customConfigFile?: string
38
39 accessToken?: string
40 refreshToken?: string
41 videoChannel?: VideoChannel
42
43 video?: {
44 id: number
45 uuid: string
46 shortUUID: string
47 name?: string
48 url?: string
49
50 account?: {
51 name: string
52 }
53
54 embedPath?: string
55 }
56
57 remoteVideo?: {
58 id: number
59 uuid: string
60 }
61
62 videos?: { id: number, uuid: string }[]
63}
64
65function parallelTests () {
66 return process.env.MOCHA_PARALLEL === 'true'
67}
68
69function flushAndRunMultipleServers (totalServers: number, configOverride?: Object) {
70 const apps = []
71 let i = 0
72
73 return new Promise<ServerInfo[]>(res => {
74 function anotherServerDone (serverNumber, app) {
75 apps[serverNumber - 1] = app
76 i++
77 if (i === totalServers) {
78 return res(apps)
79 }
80 }
81
82 for (let j = 1; j <= totalServers; j++) {
83 flushAndRunServer(j, configOverride).then(app => anotherServerDone(j, app))
84 }
85 })
86}
87
88function flushTests (serverNumber?: number) {
89 return new Promise<void>((res, rej) => {
90 const suffix = serverNumber ? ` -- ${serverNumber}` : ''
91
92 return exec('npm run clean:server:test' + suffix, (err, _stdout, stderr) => {
93 if (err || stderr) return rej(err || new Error(stderr))
94
95 return res()
96 })
97 })
98}
99
100function randomServer () {
101 const low = 10
102 const high = 10000
103
104 return randomInt(low, high)
105}
106
107function randomRTMP () {
108 const low = 1900
109 const high = 2100
110
111 return randomInt(low, high)
112}
113
114type RunServerOptions = {
115 hideLogs?: boolean
116 execArgv?: string[]
117}
118
119async function flushAndRunServer (serverNumber: number, configOverride?: Object, args = [], options: RunServerOptions = {}) {
120 const parallel = parallelTests()
121
122 const internalServerNumber = parallel ? randomServer() : serverNumber
123 const rtmpPort = parallel ? randomRTMP() : 1936
124 const port = 9000 + internalServerNumber
125
126 await flushTests(internalServerNumber)
127
128 const server: ServerInfo = {
129 app: null,
130 port,
131 internalServerNumber,
132 rtmpPort,
133 parallel,
134 serverNumber,
135 url: `http://localhost:${port}`,
136 host: `localhost:${port}`,
137 hostname: 'localhost',
138 client: {
139 id: null,
140 secret: null
141 },
142 user: {
143 username: null,
144 password: null
145 }
146 }
147
148 return runServer(server, configOverride, args, options)
149}
150
151async function runServer (server: ServerInfo, configOverrideArg?: any, args = [], options: RunServerOptions = {}) {
152 // These actions are async so we need to be sure that they have both been done
153 const serverRunString = {
154 'HTTP server listening': false
155 }
156 const key = 'Database peertube_test' + server.internalServerNumber + ' is ready'
157 serverRunString[key] = false
158
159 const regexps = {
160 client_id: 'Client id: (.+)',
161 client_secret: 'Client secret: (.+)',
162 user_username: 'Username: (.+)',
163 user_password: 'User password: (.+)'
164 }
165
166 if (server.internalServerNumber !== server.serverNumber) {
167 const basePath = join(root(), 'config')
168
169 const tmpConfigFile = join(basePath, `test-${server.internalServerNumber}.yaml`)
170 await copy(join(basePath, `test-${server.serverNumber}.yaml`), tmpConfigFile)
171
172 server.customConfigFile = tmpConfigFile
173 }
174
175 const configOverride: any = {}
176
177 if (server.parallel) {
178 Object.assign(configOverride, {
179 listen: {
180 port: server.port
181 },
182 webserver: {
183 port: server.port
184 },
185 database: {
186 suffix: '_test' + server.internalServerNumber
187 },
188 storage: {
189 tmp: `test${server.internalServerNumber}/tmp/`,
190 avatars: `test${server.internalServerNumber}/avatars/`,
191 videos: `test${server.internalServerNumber}/videos/`,
192 streaming_playlists: `test${server.internalServerNumber}/streaming-playlists/`,
193 redundancy: `test${server.internalServerNumber}/redundancy/`,
194 logs: `test${server.internalServerNumber}/logs/`,
195 previews: `test${server.internalServerNumber}/previews/`,
196 thumbnails: `test${server.internalServerNumber}/thumbnails/`,
197 torrents: `test${server.internalServerNumber}/torrents/`,
198 captions: `test${server.internalServerNumber}/captions/`,
199 cache: `test${server.internalServerNumber}/cache/`,
200 plugins: `test${server.internalServerNumber}/plugins/`
201 },
202 admin: {
203 email: `admin${server.internalServerNumber}@example.com`
204 },
205 live: {
206 rtmp: {
207 port: server.rtmpPort
208 }
209 }
210 })
211 }
212
213 if (configOverrideArg !== undefined) {
214 Object.assign(configOverride, configOverrideArg)
215 }
216
217 // Share the environment
218 const env = Object.create(process.env)
219 env['NODE_ENV'] = 'test'
220 env['NODE_APP_INSTANCE'] = server.internalServerNumber.toString()
221 env['NODE_CONFIG'] = JSON.stringify(configOverride)
222
223 const forkOptions = {
224 silent: true,
225 env,
226 detached: true,
227 execArgv: options.execArgv || []
228 }
229
230 return new Promise<ServerInfo>(res => {
231 server.app = fork(join(root(), 'dist', 'server.js'), args, forkOptions)
232 server.app.stdout.on('data', function onStdout (data) {
233 let dontContinue = false
234
235 // Capture things if we want to
236 for (const key of Object.keys(regexps)) {
237 const regexp = regexps[key]
238 const matches = data.toString().match(regexp)
239 if (matches !== null) {
240 if (key === 'client_id') server.client.id = matches[1]
241 else if (key === 'client_secret') server.client.secret = matches[1]
242 else if (key === 'user_username') server.user.username = matches[1]
243 else if (key === 'user_password') server.user.password = matches[1]
244 }
245 }
246
247 // Check if all required sentences are here
248 for (const key of Object.keys(serverRunString)) {
249 if (data.toString().indexOf(key) !== -1) serverRunString[key] = true
250 if (serverRunString[key] === false) dontContinue = true
251 }
252
253 // If no, there is maybe one thing not already initialized (client/user credentials generation...)
254 if (dontContinue === true) return
255
256 if (options.hideLogs === false) {
257 console.log(data.toString())
258 } else {
259 server.app.stdout.removeListener('data', onStdout)
260 }
261
262 process.on('exit', () => {
263 try {
264 process.kill(server.app.pid)
265 } catch { /* empty */ }
266 })
267
268 res(server)
269 })
270 })
271}
272
273async function reRunServer (server: ServerInfo, configOverride?: any) {
274 const newServer = await runServer(server, configOverride)
275 server.app = newServer.app
276 9
277 return server 10 return server
278} 11}
279 12
280async function checkTmpIsEmpty (server: ServerInfo) { 13function createMultipleServers (totalServers: number, configOverride?: Object) {
281 await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ]) 14 const serverPromises: Promise<PeerTubeServer>[] = []
282 15
283 if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) { 16 for (let i = 1; i <= totalServers; i++) {
284 await checkDirectoryIsEmpty(server, 'tmp/hls') 17 serverPromises.push(createSingleServer(i, configOverride))
285 } 18 }
286}
287
288async function checkDirectoryIsEmpty (server: ServerInfo, directory: string, exceptions: string[] = []) {
289 const testDirectory = 'test' + server.internalServerNumber
290
291 const directoryPath = join(root(), testDirectory, directory)
292 19
293 const directoryExists = await pathExists(directoryPath) 20 return Promise.all(serverPromises)
294 expect(directoryExists).to.be.true
295
296 const files = await readdir(directoryPath)
297 const filtered = files.filter(f => exceptions.includes(f) === false)
298
299 expect(filtered).to.have.lengthOf(0)
300} 21}
301 22
302function killallServers (servers: ServerInfo[]) { 23async function killallServers (servers: PeerTubeServer[]) {
303 for (const server of servers) { 24 return Promise.all(servers.map(s => s.kill()))
304 if (!server.app) continue
305
306 process.kill(-server.app.pid)
307 server.app = null
308 }
309} 25}
310 26
311async function cleanupTests (servers: ServerInfo[]) { 27async function cleanupTests (servers: PeerTubeServer[]) {
312 killallServers(servers) 28 await killallServers(servers)
313 29
314 if (isGithubCI()) { 30 if (isGithubCI()) {
315 await ensureDir('artifacts') 31 await ensureDir('artifacts')
316 } 32 }
317 33
318 const p: Promise<any>[] = [] 34 let p: Promise<any>[] = []
319 for (const server of servers) { 35 for (const server of servers) {
320 if (isGithubCI()) { 36 p = p.concat(server.servers.cleanupTests())
321 const origin = await buildServerDirectory(server, 'logs/peertube.log')
322 const destname = `peertube-${server.internalServerNumber}.log`
323 console.log('Saving logs %s.', destname)
324
325 await copy(origin, join('artifacts', destname))
326 }
327
328 if (server.parallel) {
329 p.push(flushTests(server.internalServerNumber))
330 }
331
332 if (server.customConfigFile) {
333 p.push(remove(server.customConfigFile))
334 }
335 } 37 }
336 38
337 return Promise.all(p) 39 return Promise.all(p)
338} 40}
339 41
340async function waitUntilLog (server: ServerInfo, str: string, count = 1, strictCount = true) {
341 const logfile = buildServerDirectory(server, 'logs/peertube.log')
342
343 while (true) {
344 const buf = await readFile(logfile)
345
346 const matches = buf.toString().match(new RegExp(str, 'g'))
347 if (matches && matches.length === count) return
348 if (matches && strictCount === false && matches.length >= count) return
349
350 await wait(1000)
351 }
352}
353
354async function getServerFileSize (server: ServerInfo, subPath: string) {
355 const path = buildServerDirectory(server, subPath)
356
357 return getFileSize(path)
358}
359
360function makePingRequest (server: ServerInfo) {
361 return makeGetRequest({
362 url: server.url,
363 path: '/api/v1/ping',
364 statusCodeExpected: 200
365 })
366}
367
368// --------------------------------------------------------------------------- 42// ---------------------------------------------------------------------------
369 43
370export { 44export {
371 checkDirectoryIsEmpty, 45 createSingleServer,
372 checkTmpIsEmpty, 46 createMultipleServers,
373 getServerFileSize,
374 ServerInfo,
375 parallelTests,
376 cleanupTests, 47 cleanupTests,
377 flushAndRunMultipleServers, 48 killallServers
378 flushTests,
379 makePingRequest,
380 flushAndRunServer,
381 killallServers,
382 reRunServer,
383 waitUntilLog
384} 49}
diff --git a/shared/extra-utils/server/stats-command.ts b/shared/extra-utils/server/stats-command.ts
new file mode 100644
index 000000000..64a452306
--- /dev/null
+++ b/shared/extra-utils/server/stats-command.ts
@@ -0,0 +1,25 @@
1import { HttpStatusCode, ServerStats } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class StatsCommand extends AbstractCommand {
5
6 get (options: OverrideCommandOptions & {
7 useCache?: boolean // default false
8 } = {}) {
9 const { useCache = false } = options
10 const path = '/api/v1/server/stats'
11
12 const query = {
13 t: useCache ? undefined : new Date().getTime()
14 }
15
16 return this.getRequestBody<ServerStats>({
17 ...options,
18
19 path,
20 query,
21 implicitToken: false,
22 defaultExpectedStatus: HttpStatusCode.OK_200
23 })
24 }
25}
diff --git a/shared/extra-utils/server/stats.ts b/shared/extra-utils/server/stats.ts
deleted file mode 100644
index b9dae24e2..000000000
--- a/shared/extra-utils/server/stats.ts
+++ /dev/null
@@ -1,23 +0,0 @@
1import { makeGetRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function getStats (url: string, useCache = false) {
5 const path = '/api/v1/server/stats'
6
7 const query = {
8 t: useCache ? undefined : new Date().getTime()
9 }
10
11 return makeGetRequest({
12 url,
13 path,
14 query,
15 statusCodeExpected: HttpStatusCode.OK_200
16 })
17}
18
19// ---------------------------------------------------------------------------
20
21export {
22 getStats
23}
diff --git a/shared/extra-utils/shared/abstract-command.ts b/shared/extra-utils/shared/abstract-command.ts
new file mode 100644
index 000000000..021045e49
--- /dev/null
+++ b/shared/extra-utils/shared/abstract-command.ts
@@ -0,0 +1,199 @@
1import { isAbsolute, join } from 'path'
2import { root } from '../miscs/tests'
3import {
4 makeDeleteRequest,
5 makeGetRequest,
6 makePostBodyRequest,
7 makePutBodyRequest,
8 makeUploadRequest,
9 unwrapBody,
10 unwrapText
11} from '../requests/requests'
12import { PeerTubeServer } from '../server/server'
13
14export interface OverrideCommandOptions {
15 token?: string
16 expectedStatus?: number
17}
18
19interface InternalCommonCommandOptions extends OverrideCommandOptions {
20 // Default to server.url
21 url?: string
22
23 path: string
24 // If we automatically send the server token if the token is not provided
25 implicitToken: boolean
26 defaultExpectedStatus: number
27
28 // Common optional request parameters
29 contentType?: string
30 accept?: string
31 redirects?: number
32 range?: string
33 host?: string
34 headers?: { [ name: string ]: string }
35 requestType?: string
36 xForwardedFor?: string
37}
38
39interface InternalGetCommandOptions extends InternalCommonCommandOptions {
40 query?: { [ id: string ]: any }
41}
42
43abstract class AbstractCommand {
44
45 constructor (
46 protected server: PeerTubeServer
47 ) {
48
49 }
50
51 protected getRequestBody <T> (options: InternalGetCommandOptions) {
52 return unwrapBody<T>(this.getRequest(options))
53 }
54
55 protected getRequestText (options: InternalGetCommandOptions) {
56 return unwrapText(this.getRequest(options))
57 }
58
59 protected getRawRequest (options: Omit<InternalGetCommandOptions, 'path'>) {
60 const { url, range } = options
61 const { host, protocol, pathname } = new URL(url)
62
63 return this.getRequest({
64 ...options,
65
66 token: this.buildCommonRequestToken(options),
67 defaultExpectedStatus: this.buildExpectedStatus(options),
68
69 url: `${protocol}//${host}`,
70 path: pathname,
71 range
72 })
73 }
74
75 protected getRequest (options: InternalGetCommandOptions) {
76 const { query } = options
77
78 return makeGetRequest({
79 ...this.buildCommonRequestOptions(options),
80
81 query
82 })
83 }
84
85 protected deleteRequest (options: InternalCommonCommandOptions) {
86 return makeDeleteRequest(this.buildCommonRequestOptions(options))
87 }
88
89 protected putBodyRequest (options: InternalCommonCommandOptions & {
90 fields?: { [ fieldName: string ]: any }
91 }) {
92 const { fields } = options
93
94 return makePutBodyRequest({
95 ...this.buildCommonRequestOptions(options),
96
97 fields
98 })
99 }
100
101 protected postBodyRequest (options: InternalCommonCommandOptions & {
102 fields?: { [ fieldName: string ]: any }
103 }) {
104 const { fields } = options
105
106 return makePostBodyRequest({
107 ...this.buildCommonRequestOptions(options),
108
109 fields
110 })
111 }
112
113 protected postUploadRequest (options: InternalCommonCommandOptions & {
114 fields?: { [ fieldName: string ]: any }
115 attaches?: { [ fieldName: string ]: any }
116 }) {
117 const { fields, attaches } = options
118
119 return makeUploadRequest({
120 ...this.buildCommonRequestOptions(options),
121
122 method: 'POST',
123 fields,
124 attaches
125 })
126 }
127
128 protected putUploadRequest (options: InternalCommonCommandOptions & {
129 fields?: { [ fieldName: string ]: any }
130 attaches?: { [ fieldName: string ]: any }
131 }) {
132 const { fields, attaches } = options
133
134 return makeUploadRequest({
135 ...this.buildCommonRequestOptions(options),
136
137 method: 'PUT',
138 fields,
139 attaches
140 })
141 }
142
143 protected updateImageRequest (options: InternalCommonCommandOptions & {
144 fixture: string
145 fieldname: string
146 }) {
147 const filePath = isAbsolute(options.fixture)
148 ? options.fixture
149 : join(root(), 'server', 'tests', 'fixtures', options.fixture)
150
151 return this.postUploadRequest({
152 ...options,
153
154 fields: {},
155 attaches: { [options.fieldname]: filePath }
156 })
157 }
158
159 protected buildCommonRequestOptions (options: InternalCommonCommandOptions) {
160 const { url, path, redirects, contentType, accept, range, host, headers, requestType, xForwardedFor } = options
161
162 return {
163 url: url ?? this.server.url,
164 path,
165
166 token: this.buildCommonRequestToken(options),
167 expectedStatus: this.buildExpectedStatus(options),
168
169 redirects,
170 contentType,
171 range,
172 host,
173 accept,
174 headers,
175 type: requestType,
176 xForwardedFor
177 }
178 }
179
180 protected buildCommonRequestToken (options: Pick<InternalCommonCommandOptions, 'token' | 'implicitToken'>) {
181 const { token } = options
182
183 const fallbackToken = options.implicitToken
184 ? this.server.accessToken
185 : undefined
186
187 return token !== undefined ? token : fallbackToken
188 }
189
190 protected buildExpectedStatus (options: Pick<InternalCommonCommandOptions, 'expectedStatus' | 'defaultExpectedStatus'>) {
191 const { expectedStatus, defaultExpectedStatus } = options
192
193 return expectedStatus !== undefined ? expectedStatus : defaultExpectedStatus
194 }
195}
196
197export {
198 AbstractCommand
199}
diff --git a/shared/extra-utils/shared/index.ts b/shared/extra-utils/shared/index.ts
new file mode 100644
index 000000000..e807ab4f7
--- /dev/null
+++ b/shared/extra-utils/shared/index.ts
@@ -0,0 +1 @@
export * from './abstract-command'
diff --git a/shared/extra-utils/socket/index.ts b/shared/extra-utils/socket/index.ts
new file mode 100644
index 000000000..594329b2f
--- /dev/null
+++ b/shared/extra-utils/socket/index.ts
@@ -0,0 +1 @@
export * from './socket-io-command'
diff --git a/shared/extra-utils/socket/socket-io-command.ts b/shared/extra-utils/socket/socket-io-command.ts
new file mode 100644
index 000000000..c277ead28
--- /dev/null
+++ b/shared/extra-utils/socket/socket-io-command.ts
@@ -0,0 +1,15 @@
1import { io } from 'socket.io-client'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class SocketIOCommand extends AbstractCommand {
5
6 getUserNotificationSocket (options: OverrideCommandOptions = {}) {
7 return io(this.server.url + '/user-notifications', {
8 query: { accessToken: options.token ?? this.server.accessToken }
9 })
10 }
11
12 getLiveNotificationSocket () {
13 return io(this.server.url + '/live-videos')
14 }
15}
diff --git a/shared/extra-utils/socket/socket-io.ts b/shared/extra-utils/socket/socket-io.ts
deleted file mode 100644
index 4ca93f453..000000000
--- a/shared/extra-utils/socket/socket-io.ts
+++ /dev/null
@@ -1,18 +0,0 @@
1import { io } from 'socket.io-client'
2
3function getUserNotificationSocket (serverUrl: string, accessToken: string) {
4 return io(serverUrl + '/user-notifications', {
5 query: { accessToken }
6 })
7}
8
9function getLiveNotificationSocket (serverUrl: string) {
10 return io(serverUrl + '/live-videos')
11}
12
13// ---------------------------------------------------------------------------
14
15export {
16 getUserNotificationSocket,
17 getLiveNotificationSocket
18}
diff --git a/shared/extra-utils/users/accounts-command.ts b/shared/extra-utils/users/accounts-command.ts
new file mode 100644
index 000000000..2f586104e
--- /dev/null
+++ b/shared/extra-utils/users/accounts-command.ts
@@ -0,0 +1,56 @@
1import { HttpStatusCode, ResultList } from '@shared/models'
2import { Account } from '../../models/actors'
3import { AccountVideoRate, VideoRateType } from '../../models/videos'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class AccountsCommand extends AbstractCommand {
7
8 list (options: OverrideCommandOptions & {
9 sort?: string // default -createdAt
10 } = {}) {
11 const { sort = '-createdAt' } = options
12 const path = '/api/v1/accounts'
13
14 return this.getRequestBody<ResultList<Account>>({
15 ...options,
16
17 path,
18 query: { sort },
19 implicitToken: false,
20 defaultExpectedStatus: HttpStatusCode.OK_200
21 })
22 }
23
24 get (options: OverrideCommandOptions & {
25 accountName: string
26 }) {
27 const path = '/api/v1/accounts/' + options.accountName
28
29 return this.getRequestBody<Account>({
30 ...options,
31
32 path,
33 implicitToken: false,
34 defaultExpectedStatus: HttpStatusCode.OK_200
35 })
36 }
37
38 listRatings (options: OverrideCommandOptions & {
39 accountName: string
40 rating?: VideoRateType
41 }) {
42 const { rating, accountName } = options
43 const path = '/api/v1/accounts/' + accountName + '/ratings'
44
45 const query = { rating }
46
47 return this.getRequestBody<ResultList<AccountVideoRate>>({
48 ...options,
49
50 path,
51 query,
52 implicitToken: true,
53 defaultExpectedStatus: HttpStatusCode.OK_200
54 })
55 }
56}
diff --git a/shared/extra-utils/users/accounts.ts b/shared/extra-utils/users/accounts.ts
deleted file mode 100644
index 4ea7f1402..000000000
--- a/shared/extra-utils/users/accounts.ts
+++ /dev/null
@@ -1,87 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import * as request from 'supertest'
4import { expect } from 'chai'
5import { existsSync, readdir } from 'fs-extra'
6import { join } from 'path'
7import { Account } from '../../models/actors'
8import { root } from '../miscs/miscs'
9import { makeGetRequest } from '../requests/requests'
10import { VideoRateType } from '../../models/videos'
11import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
12
13function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = HttpStatusCode.OK_200) {
14 const path = '/api/v1/accounts'
15
16 return makeGetRequest({
17 url,
18 query: { sort },
19 path,
20 statusCodeExpected
21 })
22}
23
24function getAccount (url: string, accountName: string, statusCodeExpected = HttpStatusCode.OK_200) {
25 const path = '/api/v1/accounts/' + accountName
26
27 return makeGetRequest({
28 url,
29 path,
30 statusCodeExpected
31 })
32}
33
34async function expectAccountFollows (url: string, nameWithDomain: string, followersCount: number, followingCount: number) {
35 const res = await getAccountsList(url)
36 const account = res.body.data.find((a: Account) => a.name + '@' + a.host === nameWithDomain)
37
38 const message = `${nameWithDomain} on ${url}`
39 expect(account.followersCount).to.equal(followersCount, message)
40 expect(account.followingCount).to.equal(followingCount, message)
41}
42
43async function checkActorFilesWereRemoved (filename: string, serverNumber: number) {
44 const testDirectory = 'test' + serverNumber
45
46 for (const directory of [ 'avatars' ]) {
47 const directoryPath = join(root(), testDirectory, directory)
48
49 const directoryExists = existsSync(directoryPath)
50 expect(directoryExists).to.be.true
51
52 const files = await readdir(directoryPath)
53 for (const file of files) {
54 expect(file).to.not.contain(filename)
55 }
56 }
57}
58
59function getAccountRatings (
60 url: string,
61 accountName: string,
62 accessToken: string,
63 rating?: VideoRateType,
64 statusCodeExpected = HttpStatusCode.OK_200
65) {
66 const path = '/api/v1/accounts/' + accountName + '/ratings'
67
68 const query = rating ? { rating } : {}
69
70 return request(url)
71 .get(path)
72 .query(query)
73 .set('Accept', 'application/json')
74 .set('Authorization', 'Bearer ' + accessToken)
75 .expect(statusCodeExpected)
76 .expect('Content-Type', /json/)
77}
78
79// ---------------------------------------------------------------------------
80
81export {
82 getAccount,
83 expectAccountFollows,
84 getAccountsList,
85 checkActorFilesWereRemoved,
86 getAccountRatings
87}
diff --git a/shared/extra-utils/users/actors.ts b/shared/extra-utils/users/actors.ts
new file mode 100644
index 000000000..cfcc7d0a7
--- /dev/null
+++ b/shared/extra-utils/users/actors.ts
@@ -0,0 +1,73 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { pathExists, readdir } from 'fs-extra'
5import { join } from 'path'
6import { root } from '@server/helpers/core-utils'
7import { Account, VideoChannel } from '@shared/models'
8import { PeerTubeServer } from '../server'
9
10async function expectChannelsFollows (options: {
11 server: PeerTubeServer
12 handle: string
13 followers: number
14 following: number
15}) {
16 const { server } = options
17 const { data } = await server.channels.list()
18
19 return expectActorFollow({ ...options, data })
20}
21
22async function expectAccountFollows (options: {
23 server: PeerTubeServer
24 handle: string
25 followers: number
26 following: number
27}) {
28 const { server } = options
29 const { data } = await server.accounts.list()
30
31 return expectActorFollow({ ...options, data })
32}
33
34async function checkActorFilesWereRemoved (filename: string, serverNumber: number) {
35 const testDirectory = 'test' + serverNumber
36
37 for (const directory of [ 'avatars' ]) {
38 const directoryPath = join(root(), testDirectory, directory)
39
40 const directoryExists = await pathExists(directoryPath)
41 expect(directoryExists).to.be.true
42
43 const files = await readdir(directoryPath)
44 for (const file of files) {
45 expect(file).to.not.contain(filename)
46 }
47 }
48}
49
50export {
51 expectAccountFollows,
52 expectChannelsFollows,
53 checkActorFilesWereRemoved
54}
55
56// ---------------------------------------------------------------------------
57
58function expectActorFollow (options: {
59 server: PeerTubeServer
60 data: (Account | VideoChannel)[]
61 handle: string
62 followers: number
63 following: number
64}) {
65 const { server, data, handle, followers, following } = options
66
67 const actor = data.find(a => a.name + '@' + a.host === handle)
68 const message = `${handle} on ${server.url}`
69
70 expect(actor, message).to.exist
71 expect(actor.followersCount).to.equal(followers, message)
72 expect(actor.followingCount).to.equal(following, message)
73}
diff --git a/shared/extra-utils/users/blocklist-command.ts b/shared/extra-utils/users/blocklist-command.ts
new file mode 100644
index 000000000..14491a1ae
--- /dev/null
+++ b/shared/extra-utils/users/blocklist-command.ts
@@ -0,0 +1,139 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { AccountBlock, HttpStatusCode, ResultList, ServerBlock } from '@shared/models'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6type ListBlocklistOptions = OverrideCommandOptions & {
7 start: number
8 count: number
9 sort: string // default -createdAt
10}
11
12export class BlocklistCommand extends AbstractCommand {
13
14 listMyAccountBlocklist (options: ListBlocklistOptions) {
15 const path = '/api/v1/users/me/blocklist/accounts'
16
17 return this.listBlocklist<AccountBlock>(options, path)
18 }
19
20 listMyServerBlocklist (options: ListBlocklistOptions) {
21 const path = '/api/v1/users/me/blocklist/servers'
22
23 return this.listBlocklist<ServerBlock>(options, path)
24 }
25
26 listServerAccountBlocklist (options: ListBlocklistOptions) {
27 const path = '/api/v1/server/blocklist/accounts'
28
29 return this.listBlocklist<AccountBlock>(options, path)
30 }
31
32 listServerServerBlocklist (options: ListBlocklistOptions) {
33 const path = '/api/v1/server/blocklist/servers'
34
35 return this.listBlocklist<ServerBlock>(options, path)
36 }
37
38 // ---------------------------------------------------------------------------
39
40 addToMyBlocklist (options: OverrideCommandOptions & {
41 account?: string
42 server?: string
43 }) {
44 const { account, server } = options
45
46 const path = account
47 ? '/api/v1/users/me/blocklist/accounts'
48 : '/api/v1/users/me/blocklist/servers'
49
50 return this.postBodyRequest({
51 ...options,
52
53 path,
54 fields: {
55 accountName: account,
56 host: server
57 },
58 implicitToken: true,
59 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
60 })
61 }
62
63 addToServerBlocklist (options: OverrideCommandOptions & {
64 account?: string
65 server?: string
66 }) {
67 const { account, server } = options
68
69 const path = account
70 ? '/api/v1/server/blocklist/accounts'
71 : '/api/v1/server/blocklist/servers'
72
73 return this.postBodyRequest({
74 ...options,
75
76 path,
77 fields: {
78 accountName: account,
79 host: server
80 },
81 implicitToken: true,
82 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
83 })
84 }
85
86 // ---------------------------------------------------------------------------
87
88 removeFromMyBlocklist (options: OverrideCommandOptions & {
89 account?: string
90 server?: string
91 }) {
92 const { account, server } = options
93
94 const path = account
95 ? '/api/v1/users/me/blocklist/accounts/' + account
96 : '/api/v1/users/me/blocklist/servers/' + server
97
98 return this.deleteRequest({
99 ...options,
100
101 path,
102 implicitToken: true,
103 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
104 })
105 }
106
107 removeFromServerBlocklist (options: OverrideCommandOptions & {
108 account?: string
109 server?: string
110 }) {
111 const { account, server } = options
112
113 const path = account
114 ? '/api/v1/server/blocklist/accounts/' + account
115 : '/api/v1/server/blocklist/servers/' + server
116
117 return this.deleteRequest({
118 ...options,
119
120 path,
121 implicitToken: true,
122 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
123 })
124 }
125
126 private listBlocklist <T> (options: ListBlocklistOptions, path: string) {
127 const { start, count, sort = '-createdAt' } = options
128
129 return this.getRequestBody<ResultList<T>>({
130 ...options,
131
132 path,
133 query: { start, count, sort },
134 implicitToken: true,
135 defaultExpectedStatus: HttpStatusCode.OK_200
136 })
137 }
138
139}
diff --git a/shared/extra-utils/users/blocklist.ts b/shared/extra-utils/users/blocklist.ts
deleted file mode 100644
index bdf7ee58a..000000000
--- a/shared/extra-utils/users/blocklist.ts
+++ /dev/null
@@ -1,238 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { makeGetRequest, makeDeleteRequest, makePostBodyRequest } from '../requests/requests'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5
6function getAccountBlocklistByAccount (
7 url: string,
8 token: string,
9 start: number,
10 count: number,
11 sort = '-createdAt',
12 statusCodeExpected = HttpStatusCode.OK_200
13) {
14 const path = '/api/v1/users/me/blocklist/accounts'
15
16 return makeGetRequest({
17 url,
18 token,
19 query: { start, count, sort },
20 path,
21 statusCodeExpected
22 })
23}
24
25function addAccountToAccountBlocklist (
26 url: string,
27 token: string,
28 accountToBlock: string,
29 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
30) {
31 const path = '/api/v1/users/me/blocklist/accounts'
32
33 return makePostBodyRequest({
34 url,
35 path,
36 token,
37 fields: {
38 accountName: accountToBlock
39 },
40 statusCodeExpected
41 })
42}
43
44function removeAccountFromAccountBlocklist (
45 url: string,
46 token: string,
47 accountToUnblock: string,
48 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
49) {
50 const path = '/api/v1/users/me/blocklist/accounts/' + accountToUnblock
51
52 return makeDeleteRequest({
53 url,
54 path,
55 token,
56 statusCodeExpected
57 })
58}
59
60function getServerBlocklistByAccount (
61 url: string,
62 token: string,
63 start: number,
64 count: number,
65 sort = '-createdAt',
66 statusCodeExpected = HttpStatusCode.OK_200
67) {
68 const path = '/api/v1/users/me/blocklist/servers'
69
70 return makeGetRequest({
71 url,
72 token,
73 query: { start, count, sort },
74 path,
75 statusCodeExpected
76 })
77}
78
79function addServerToAccountBlocklist (
80 url: string,
81 token: string,
82 serverToBlock: string,
83 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
84) {
85 const path = '/api/v1/users/me/blocklist/servers'
86
87 return makePostBodyRequest({
88 url,
89 path,
90 token,
91 fields: {
92 host: serverToBlock
93 },
94 statusCodeExpected
95 })
96}
97
98function removeServerFromAccountBlocklist (
99 url: string,
100 token: string,
101 serverToBlock: string,
102 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
103) {
104 const path = '/api/v1/users/me/blocklist/servers/' + serverToBlock
105
106 return makeDeleteRequest({
107 url,
108 path,
109 token,
110 statusCodeExpected
111 })
112}
113
114function getAccountBlocklistByServer (
115 url: string,
116 token: string,
117 start: number,
118 count: number,
119 sort = '-createdAt',
120 statusCodeExpected = HttpStatusCode.OK_200
121) {
122 const path = '/api/v1/server/blocklist/accounts'
123
124 return makeGetRequest({
125 url,
126 token,
127 query: { start, count, sort },
128 path,
129 statusCodeExpected
130 })
131}
132
133function addAccountToServerBlocklist (
134 url: string,
135 token: string,
136 accountToBlock: string,
137 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
138) {
139 const path = '/api/v1/server/blocklist/accounts'
140
141 return makePostBodyRequest({
142 url,
143 path,
144 token,
145 fields: {
146 accountName: accountToBlock
147 },
148 statusCodeExpected
149 })
150}
151
152function removeAccountFromServerBlocklist (
153 url: string,
154 token: string,
155 accountToUnblock: string,
156 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
157) {
158 const path = '/api/v1/server/blocklist/accounts/' + accountToUnblock
159
160 return makeDeleteRequest({
161 url,
162 path,
163 token,
164 statusCodeExpected
165 })
166}
167
168function getServerBlocklistByServer (
169 url: string,
170 token: string,
171 start: number,
172 count: number,
173 sort = '-createdAt',
174 statusCodeExpected = HttpStatusCode.OK_200
175) {
176 const path = '/api/v1/server/blocklist/servers'
177
178 return makeGetRequest({
179 url,
180 token,
181 query: { start, count, sort },
182 path,
183 statusCodeExpected
184 })
185}
186
187function addServerToServerBlocklist (
188 url: string,
189 token: string,
190 serverToBlock: string,
191 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
192) {
193 const path = '/api/v1/server/blocklist/servers'
194
195 return makePostBodyRequest({
196 url,
197 path,
198 token,
199 fields: {
200 host: serverToBlock
201 },
202 statusCodeExpected
203 })
204}
205
206function removeServerFromServerBlocklist (
207 url: string,
208 token: string,
209 serverToBlock: string,
210 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
211) {
212 const path = '/api/v1/server/blocklist/servers/' + serverToBlock
213
214 return makeDeleteRequest({
215 url,
216 path,
217 token,
218 statusCodeExpected
219 })
220}
221
222// ---------------------------------------------------------------------------
223
224export {
225 getAccountBlocklistByAccount,
226 addAccountToAccountBlocklist,
227 removeAccountFromAccountBlocklist,
228 getServerBlocklistByAccount,
229 addServerToAccountBlocklist,
230 removeServerFromAccountBlocklist,
231
232 getAccountBlocklistByServer,
233 addAccountToServerBlocklist,
234 removeAccountFromServerBlocklist,
235 getServerBlocklistByServer,
236 addServerToServerBlocklist,
237 removeServerFromServerBlocklist
238}
diff --git a/shared/extra-utils/users/index.ts b/shared/extra-utils/users/index.ts
new file mode 100644
index 000000000..460a06f70
--- /dev/null
+++ b/shared/extra-utils/users/index.ts
@@ -0,0 +1,9 @@
1export * from './accounts-command'
2export * from './actors'
3export * from './blocklist-command'
4export * from './login'
5export * from './login-command'
6export * from './notifications'
7export * from './notifications-command'
8export * from './subscriptions-command'
9export * from './users-command'
diff --git a/shared/extra-utils/users/login-command.ts b/shared/extra-utils/users/login-command.ts
new file mode 100644
index 000000000..143f72a59
--- /dev/null
+++ b/shared/extra-utils/users/login-command.ts
@@ -0,0 +1,132 @@
1import { HttpStatusCode, PeerTubeProblemDocument } from '@shared/models'
2import { unwrapBody } from '../requests'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class LoginCommand extends AbstractCommand {
6
7 login (options: OverrideCommandOptions & {
8 client?: { id?: string, secret?: string }
9 user?: { username: string, password?: string }
10 } = {}) {
11 const { client = this.server.store.client, user = this.server.store.user } = options
12 const path = '/api/v1/users/token'
13
14 const body = {
15 client_id: client.id,
16 client_secret: client.secret,
17 username: user.username,
18 password: user.password ?? 'password',
19 response_type: 'code',
20 grant_type: 'password',
21 scope: 'upload'
22 }
23
24 return unwrapBody<{ access_token: string, refresh_token: string } & PeerTubeProblemDocument>(this.postBodyRequest({
25 ...options,
26
27 path,
28 requestType: 'form',
29 fields: body,
30 implicitToken: false,
31 defaultExpectedStatus: HttpStatusCode.OK_200
32 }))
33 }
34
35 getAccessToken (arg1?: { username: string, password?: string }): Promise<string>
36 getAccessToken (arg1: string, password?: string): Promise<string>
37 async getAccessToken (arg1?: { username: string, password?: string } | string, password?: string) {
38 let user: { username: string, password?: string }
39
40 if (!arg1) user = this.server.store.user
41 else if (typeof arg1 === 'object') user = arg1
42 else user = { username: arg1, password }
43
44 try {
45 const body = await this.login({ user })
46
47 return body.access_token
48 } catch (err) {
49 throw new Error(`Cannot authenticate. Please check your username/password. (${err})`)
50 }
51 }
52
53 loginUsingExternalToken (options: OverrideCommandOptions & {
54 username: string
55 externalAuthToken: string
56 }) {
57 const { username, externalAuthToken } = options
58 const path = '/api/v1/users/token'
59
60 const body = {
61 client_id: this.server.store.client.id,
62 client_secret: this.server.store.client.secret,
63 username: username,
64 response_type: 'code',
65 grant_type: 'password',
66 scope: 'upload',
67 externalAuthToken
68 }
69
70 return this.postBodyRequest({
71 ...options,
72
73 path,
74 requestType: 'form',
75 fields: body,
76 implicitToken: false,
77 defaultExpectedStatus: HttpStatusCode.OK_200
78 })
79 }
80
81 logout (options: OverrideCommandOptions & {
82 token: string
83 }) {
84 const path = '/api/v1/users/revoke-token'
85
86 return unwrapBody<{ redirectUrl: string }>(this.postBodyRequest({
87 ...options,
88
89 path,
90 requestType: 'form',
91 implicitToken: false,
92 defaultExpectedStatus: HttpStatusCode.OK_200
93 }))
94 }
95
96 refreshToken (options: OverrideCommandOptions & {
97 refreshToken: string
98 }) {
99 const path = '/api/v1/users/token'
100
101 const body = {
102 client_id: this.server.store.client.id,
103 client_secret: this.server.store.client.secret,
104 refresh_token: options.refreshToken,
105 response_type: 'code',
106 grant_type: 'refresh_token'
107 }
108
109 return this.postBodyRequest({
110 ...options,
111
112 path,
113 requestType: 'form',
114 fields: body,
115 implicitToken: false,
116 defaultExpectedStatus: HttpStatusCode.OK_200
117 })
118 }
119
120 getClient (options: OverrideCommandOptions = {}) {
121 const path = '/api/v1/oauth-clients/local'
122
123 return this.getRequestBody<{ client_id: string, client_secret: string }>({
124 ...options,
125
126 path,
127 host: this.server.host,
128 implicitToken: false,
129 defaultExpectedStatus: HttpStatusCode.OK_200
130 })
131 }
132}
diff --git a/shared/extra-utils/users/login.ts b/shared/extra-utils/users/login.ts
index 39e1a2747..f1df027d3 100644
--- a/shared/extra-utils/users/login.ts
+++ b/shared/extra-utils/users/login.ts
@@ -1,133 +1,19 @@
1import * as request from 'supertest' 1import { PeerTubeServer } from '../server/server'
2 2
3import { ServerInfo } from '../server/servers' 3function setAccessTokensToServers (servers: PeerTubeServer[]) {
4import { getClient } from '../server/clients'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6
7type Client = { id: string, secret: string }
8type User = { username: string, password: string }
9type Server = { url: string, client: Client, user: User }
10
11function login (url: string, client: Client, user: User, expectedStatus = HttpStatusCode.OK_200) {
12 const path = '/api/v1/users/token'
13
14 const body = {
15 client_id: client.id,
16 client_secret: client.secret,
17 username: user.username,
18 password: user.password,
19 response_type: 'code',
20 grant_type: 'password',
21 scope: 'upload'
22 }
23
24 return request(url)
25 .post(path)
26 .type('form')
27 .send(body)
28 .expect(expectedStatus)
29}
30
31function logout (url: string, token: string, expectedStatus = HttpStatusCode.OK_200) {
32 const path = '/api/v1/users/revoke-token'
33
34 return request(url)
35 .post(path)
36 .set('Authorization', 'Bearer ' + token)
37 .type('form')
38 .expect(expectedStatus)
39}
40
41async function serverLogin (server: Server) {
42 const res = await login(server.url, server.client, server.user, HttpStatusCode.OK_200)
43
44 return res.body.access_token as string
45}
46
47function refreshToken (server: ServerInfo, refreshToken: string, expectedStatus = HttpStatusCode.OK_200) {
48 const path = '/api/v1/users/token'
49
50 const body = {
51 client_id: server.client.id,
52 client_secret: server.client.secret,
53 refresh_token: refreshToken,
54 response_type: 'code',
55 grant_type: 'refresh_token'
56 }
57
58 return request(server.url)
59 .post(path)
60 .type('form')
61 .send(body)
62 .expect(expectedStatus)
63}
64
65async function userLogin (server: Server, user: User, expectedStatus = HttpStatusCode.OK_200) {
66 const res = await login(server.url, server.client, user, expectedStatus)
67
68 return res.body.access_token as string
69}
70
71async function getAccessToken (url: string, username: string, password: string) {
72 const resClient = await getClient(url)
73 const client = {
74 id: resClient.body.client_id,
75 secret: resClient.body.client_secret
76 }
77
78 const user = { username, password }
79
80 try {
81 const res = await login(url, client, user)
82 return res.body.access_token
83 } catch (err) {
84 throw new Error('Cannot authenticate. Please check your username/password.')
85 }
86}
87
88function setAccessTokensToServers (servers: ServerInfo[]) {
89 const tasks: Promise<any>[] = [] 4 const tasks: Promise<any>[] = []
90 5
91 for (const server of servers) { 6 for (const server of servers) {
92 const p = serverLogin(server).then(t => { server.accessToken = t }) 7 const p = server.login.getAccessToken()
8 .then(t => { server.accessToken = t })
93 tasks.push(p) 9 tasks.push(p)
94 } 10 }
95 11
96 return Promise.all(tasks) 12 return Promise.all(tasks)
97} 13}
98 14
99function loginUsingExternalToken (server: Server, username: string, externalAuthToken: string, expectedStatus = HttpStatusCode.OK_200) {
100 const path = '/api/v1/users/token'
101
102 const body = {
103 client_id: server.client.id,
104 client_secret: server.client.secret,
105 username: username,
106 response_type: 'code',
107 grant_type: 'password',
108 scope: 'upload',
109 externalAuthToken
110 }
111
112 return request(server.url)
113 .post(path)
114 .type('form')
115 .send(body)
116 .expect(expectedStatus)
117}
118
119// --------------------------------------------------------------------------- 15// ---------------------------------------------------------------------------
120 16
121export { 17export {
122 login, 18 setAccessTokensToServers
123 logout,
124 serverLogin,
125 refreshToken,
126 userLogin,
127 getAccessToken,
128 setAccessTokensToServers,
129 Server,
130 Client,
131 User,
132 loginUsingExternalToken
133} 19}
diff --git a/shared/extra-utils/users/notifications-command.ts b/shared/extra-utils/users/notifications-command.ts
new file mode 100644
index 000000000..2d79a3747
--- /dev/null
+++ b/shared/extra-utils/users/notifications-command.ts
@@ -0,0 +1,86 @@
1import { HttpStatusCode, ResultList } from '@shared/models'
2import { UserNotification, UserNotificationSetting } from '../../models/users'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class NotificationsCommand extends AbstractCommand {
6
7 updateMySettings (options: OverrideCommandOptions & {
8 settings: UserNotificationSetting
9 }) {
10 const path = '/api/v1/users/me/notification-settings'
11
12 return this.putBodyRequest({
13 ...options,
14
15 path,
16 fields: options.settings,
17 implicitToken: true,
18 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
19 })
20 }
21
22 list (options: OverrideCommandOptions & {
23 start?: number
24 count?: number
25 unread?: boolean
26 sort?: string
27 }) {
28 const { start, count, unread, sort = '-createdAt' } = options
29 const path = '/api/v1/users/me/notifications'
30
31 return this.getRequestBody<ResultList<UserNotification>>({
32 ...options,
33
34 path,
35 query: {
36 start,
37 count,
38 sort,
39 unread
40 },
41 implicitToken: true,
42 defaultExpectedStatus: HttpStatusCode.OK_200
43 })
44 }
45
46 markAsRead (options: OverrideCommandOptions & {
47 ids: number[]
48 }) {
49 const { ids } = options
50 const path = '/api/v1/users/me/notifications/read'
51
52 return this.postBodyRequest({
53 ...options,
54
55 path,
56 fields: { ids },
57 implicitToken: true,
58 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
59 })
60 }
61
62 markAsReadAll (options: OverrideCommandOptions) {
63 const path = '/api/v1/users/me/notifications/read-all'
64
65 return this.postBodyRequest({
66 ...options,
67
68 path,
69 implicitToken: true,
70 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
71 })
72 }
73
74 async getLastest (options: OverrideCommandOptions = {}) {
75 const { total, data } = await this.list({
76 ...options,
77 start: 0,
78 count: 1,
79 sort: '-createdAt'
80 })
81
82 if (total === 0) return undefined
83
84 return data[0]
85 }
86}
diff --git a/shared/extra-utils/users/user-notifications.ts b/shared/extra-utils/users/notifications.ts
index 844f4442d..4c42fad3e 100644
--- a/shared/extra-utils/users/user-notifications.ts
+++ b/shared/extra-utils/users/notifications.ts
@@ -3,91 +3,36 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { inspect } from 'util' 4import { inspect } from 'util'
5import { AbuseState, PluginType } from '@shared/models' 5import { AbuseState, PluginType } from '@shared/models'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
7import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users' 6import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users'
8import { MockSmtpServer } from '../miscs/email' 7import { MockSmtpServer } from '../mock-servers/mock-email'
9import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests' 8import { PeerTubeServer } from '../server'
10import { doubleFollow } from '../server/follows' 9import { doubleFollow } from '../server/follows'
11import { flushAndRunMultipleServers, ServerInfo } from '../server/servers' 10import { createMultipleServers } from '../server/servers'
12import { getUserNotificationSocket } from '../socket/socket-io' 11import { setAccessTokensToServers } from './login'
13import { setAccessTokensToServers, userLogin } from './login'
14import { createUser, getMyUserInformation } from './users'
15 12
16function updateMyNotificationSettings ( 13function getAllNotificationsSettings (): UserNotificationSetting {
17 url: string, 14 return {
18 token: string, 15 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
19 settings: UserNotificationSetting, 16 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
20 statusCodeExpected = HttpStatusCode.NO_CONTENT_204 17 abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
21) { 18 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
22 const path = '/api/v1/users/me/notification-settings' 19 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
23 20 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
24 return makePutBodyRequest({ 21 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
25 url, 22 commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
26 path, 23 newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
27 token, 24 newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
28 fields: settings, 25 newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
29 statusCodeExpected 26 abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
30 }) 27 abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
31} 28 autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
32 29 newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
33async function getUserNotifications ( 30 newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
34 url: string, 31 }
35 token: string,
36 start: number,
37 count: number,
38 unread?: boolean,
39 sort = '-createdAt',
40 statusCodeExpected = HttpStatusCode.OK_200
41) {
42 const path = '/api/v1/users/me/notifications'
43
44 return makeGetRequest({
45 url,
46 path,
47 token,
48 query: {
49 start,
50 count,
51 sort,
52 unread
53 },
54 statusCodeExpected
55 })
56}
57
58function markAsReadNotifications (url: string, token: string, ids: number[], statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
59 const path = '/api/v1/users/me/notifications/read'
60
61 return makePostBodyRequest({
62 url,
63 path,
64 token,
65 fields: { ids },
66 statusCodeExpected
67 })
68}
69
70function markAsReadAllNotifications (url: string, token: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
71 const path = '/api/v1/users/me/notifications/read-all'
72
73 return makePostBodyRequest({
74 url,
75 path,
76 token,
77 statusCodeExpected
78 })
79}
80
81async function getLastNotification (serverUrl: string, accessToken: string) {
82 const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt')
83
84 if (res.body.total === 0) return undefined
85
86 return res.body.data[0] as UserNotification
87} 32}
88 33
89type CheckerBaseParams = { 34type CheckerBaseParams = {
90 server: ServerInfo 35 server: PeerTubeServer
91 emails: any[] 36 emails: any[]
92 socketNotifications: UserNotification[] 37 socketNotifications: UserNotification[]
93 token: string 38 token: string
@@ -105,7 +50,7 @@ async function checkNotification (
105 const check = base.check || { web: true, mail: true } 50 const check = base.check || { web: true, mail: true }
106 51
107 if (check.web) { 52 if (check.web) {
108 const notification = await getLastNotification(base.server.url, base.token) 53 const notification = await base.server.notifications.getLastest({ token: base.token })
109 54
110 if (notification || checkType !== 'absence') { 55 if (notification || checkType !== 'absence') {
111 notificationChecker(notification, checkType) 56 notificationChecker(notification, checkType)
@@ -681,27 +626,6 @@ async function checkNewPluginVersion (base: CheckerBaseParams, pluginType: Plugi
681 await checkNotification(base, notificationChecker, emailNotificationFinder, type) 626 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
682} 627}
683 628
684function getAllNotificationsSettings (): UserNotificationSetting {
685 return {
686 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
687 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
688 abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
689 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
690 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
691 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
692 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
693 commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
694 newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
695 newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
696 newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
697 abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
698 abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
699 autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
700 newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
701 newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
702 }
703}
704
705async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) { 629async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) {
706 const userNotifications: UserNotification[] = [] 630 const userNotifications: UserNotification[] = []
707 const adminNotifications: UserNotification[] = [] 631 const adminNotifications: UserNotification[] = []
@@ -719,7 +643,7 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an
719 limit: 20 643 limit: 20
720 } 644 }
721 } 645 }
722 const servers = await flushAndRunMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg)) 646 const servers = await createMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg))
723 647
724 await setAccessTokensToServers(servers) 648 await setAccessTokensToServers(servers)
725 649
@@ -727,42 +651,33 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an
727 await doubleFollow(servers[0], servers[1]) 651 await doubleFollow(servers[0], servers[1])
728 } 652 }
729 653
730 const user = { 654 const user = { username: 'user_1', password: 'super password' }
731 username: 'user_1', 655 await servers[0].users.create({ ...user, videoQuota: 10 * 1000 * 1000 })
732 password: 'super password' 656 const userAccessToken = await servers[0].login.getAccessToken(user)
733 }
734 await createUser({
735 url: servers[0].url,
736 accessToken: servers[0].accessToken,
737 username: user.username,
738 password: user.password,
739 videoQuota: 10 * 1000 * 1000
740 })
741 const userAccessToken = await userLogin(servers[0], user)
742 657
743 await updateMyNotificationSettings(servers[0].url, userAccessToken, getAllNotificationsSettings()) 658 await servers[0].notifications.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() })
744 await updateMyNotificationSettings(servers[0].url, servers[0].accessToken, getAllNotificationsSettings()) 659 await servers[0].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
745 660
746 if (serversCount > 1) { 661 if (serversCount > 1) {
747 await updateMyNotificationSettings(servers[1].url, servers[1].accessToken, getAllNotificationsSettings()) 662 await servers[1].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
748 } 663 }
749 664
750 { 665 {
751 const socket = getUserNotificationSocket(servers[0].url, userAccessToken) 666 const socket = servers[0].socketIO.getUserNotificationSocket({ token: userAccessToken })
752 socket.on('new-notification', n => userNotifications.push(n)) 667 socket.on('new-notification', n => userNotifications.push(n))
753 } 668 }
754 { 669 {
755 const socket = getUserNotificationSocket(servers[0].url, servers[0].accessToken) 670 const socket = servers[0].socketIO.getUserNotificationSocket()
756 socket.on('new-notification', n => adminNotifications.push(n)) 671 socket.on('new-notification', n => adminNotifications.push(n))
757 } 672 }
758 673
759 if (serversCount > 1) { 674 if (serversCount > 1) {
760 const socket = getUserNotificationSocket(servers[1].url, servers[1].accessToken) 675 const socket = servers[1].socketIO.getUserNotificationSocket()
761 socket.on('new-notification', n => adminNotificationsServer2.push(n)) 676 socket.on('new-notification', n => adminNotificationsServer2.push(n))
762 } 677 }
763 678
764 const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken) 679 const { videoChannels } = await servers[0].users.getMyInfo()
765 const channelId = resChannel.body.videoChannels[0].id 680 const channelId = videoChannels[0].id
766 681
767 return { 682 return {
768 userNotifications, 683 userNotifications,
@@ -778,11 +693,11 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an
778// --------------------------------------------------------------------------- 693// ---------------------------------------------------------------------------
779 694
780export { 695export {
696 getAllNotificationsSettings,
697
781 CheckerBaseParams, 698 CheckerBaseParams,
782 CheckerType, 699 CheckerType,
783 getAllNotificationsSettings,
784 checkNotification, 700 checkNotification,
785 markAsReadAllNotifications,
786 checkMyVideoImportIsFinished, 701 checkMyVideoImportIsFinished,
787 checkUserRegistered, 702 checkUserRegistered,
788 checkAutoInstanceFollowing, 703 checkAutoInstanceFollowing,
@@ -792,14 +707,10 @@ export {
792 checkNewCommentOnMyVideo, 707 checkNewCommentOnMyVideo,
793 checkNewBlacklistOnMyVideo, 708 checkNewBlacklistOnMyVideo,
794 checkCommentMention, 709 checkCommentMention,
795 updateMyNotificationSettings,
796 checkNewVideoAbuseForModerators, 710 checkNewVideoAbuseForModerators,
797 checkVideoAutoBlacklistForModerators, 711 checkVideoAutoBlacklistForModerators,
798 checkNewAbuseMessage, 712 checkNewAbuseMessage,
799 checkAbuseStateChange, 713 checkAbuseStateChange,
800 getUserNotifications,
801 markAsReadNotifications,
802 getLastNotification,
803 checkNewInstanceFollower, 714 checkNewInstanceFollower,
804 prepareNotificationsTest, 715 prepareNotificationsTest,
805 checkNewCommentAbuseForModerators, 716 checkNewCommentAbuseForModerators,
diff --git a/shared/extra-utils/users/subscriptions-command.ts b/shared/extra-utils/users/subscriptions-command.ts
new file mode 100644
index 000000000..edc60e612
--- /dev/null
+++ b/shared/extra-utils/users/subscriptions-command.ts
@@ -0,0 +1,99 @@
1import { HttpStatusCode, ResultList, Video, VideoChannel } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class SubscriptionsCommand extends AbstractCommand {
5
6 add (options: OverrideCommandOptions & {
7 targetUri: string
8 }) {
9 const path = '/api/v1/users/me/subscriptions'
10
11 return this.postBodyRequest({
12 ...options,
13
14 path,
15 fields: { uri: options.targetUri },
16 implicitToken: true,
17 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
18 })
19 }
20
21 list (options: OverrideCommandOptions & {
22 sort?: string // default -createdAt
23 search?: string
24 } = {}) {
25 const { sort = '-createdAt', search } = options
26 const path = '/api/v1/users/me/subscriptions'
27
28 return this.getRequestBody<ResultList<VideoChannel>>({
29 ...options,
30
31 path,
32 query: {
33 sort,
34 search
35 },
36 implicitToken: true,
37 defaultExpectedStatus: HttpStatusCode.OK_200
38 })
39 }
40
41 listVideos (options: OverrideCommandOptions & {
42 sort?: string // default -createdAt
43 } = {}) {
44 const { sort = '-createdAt' } = options
45 const path = '/api/v1/users/me/subscriptions/videos'
46
47 return this.getRequestBody<ResultList<Video>>({
48 ...options,
49
50 path,
51 query: { sort },
52 implicitToken: true,
53 defaultExpectedStatus: HttpStatusCode.OK_200
54 })
55 }
56
57 get (options: OverrideCommandOptions & {
58 uri: string
59 }) {
60 const path = '/api/v1/users/me/subscriptions/' + options.uri
61
62 return this.getRequestBody<VideoChannel>({
63 ...options,
64
65 path,
66 implicitToken: true,
67 defaultExpectedStatus: HttpStatusCode.OK_200
68 })
69 }
70
71 remove (options: OverrideCommandOptions & {
72 uri: string
73 }) {
74 const path = '/api/v1/users/me/subscriptions/' + options.uri
75
76 return this.deleteRequest({
77 ...options,
78
79 path,
80 implicitToken: true,
81 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
82 })
83 }
84
85 exist (options: OverrideCommandOptions & {
86 uris: string[]
87 }) {
88 const path = '/api/v1/users/me/subscriptions/exist'
89
90 return this.getRequestBody<{ [id: string ]: boolean }>({
91 ...options,
92
93 path,
94 query: { 'uris[]': options.uris },
95 implicitToken: true,
96 defaultExpectedStatus: HttpStatusCode.OK_200
97 })
98 }
99}
diff --git a/shared/extra-utils/users/user-subscriptions.ts b/shared/extra-utils/users/user-subscriptions.ts
deleted file mode 100644
index edc7a3562..000000000
--- a/shared/extra-utils/users/user-subscriptions.ts
+++ /dev/null
@@ -1,93 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makePostBodyRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function addUserSubscription (url: string, token: string, targetUri: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
5 const path = '/api/v1/users/me/subscriptions'
6
7 return makePostBodyRequest({
8 url,
9 path,
10 token,
11 statusCodeExpected,
12 fields: { uri: targetUri }
13 })
14}
15
16function listUserSubscriptions (parameters: {
17 url: string
18 token: string
19 sort?: string
20 search?: string
21 statusCodeExpected?: number
22}) {
23 const { url, token, sort = '-createdAt', search, statusCodeExpected = HttpStatusCode.OK_200 } = parameters
24 const path = '/api/v1/users/me/subscriptions'
25
26 return makeGetRequest({
27 url,
28 path,
29 token,
30 statusCodeExpected,
31 query: {
32 sort,
33 search
34 }
35 })
36}
37
38function listUserSubscriptionVideos (url: string, token: string, sort = '-createdAt', statusCodeExpected = HttpStatusCode.OK_200) {
39 const path = '/api/v1/users/me/subscriptions/videos'
40
41 return makeGetRequest({
42 url,
43 path,
44 token,
45 statusCodeExpected,
46 query: { sort }
47 })
48}
49
50function getUserSubscription (url: string, token: string, uri: string, statusCodeExpected = HttpStatusCode.OK_200) {
51 const path = '/api/v1/users/me/subscriptions/' + uri
52
53 return makeGetRequest({
54 url,
55 path,
56 token,
57 statusCodeExpected
58 })
59}
60
61function removeUserSubscription (url: string, token: string, uri: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
62 const path = '/api/v1/users/me/subscriptions/' + uri
63
64 return makeDeleteRequest({
65 url,
66 path,
67 token,
68 statusCodeExpected
69 })
70}
71
72function areSubscriptionsExist (url: string, token: string, uris: string[], statusCodeExpected = HttpStatusCode.OK_200) {
73 const path = '/api/v1/users/me/subscriptions/exist'
74
75 return makeGetRequest({
76 url,
77 path,
78 query: { 'uris[]': uris },
79 token,
80 statusCodeExpected
81 })
82}
83
84// ---------------------------------------------------------------------------
85
86export {
87 areSubscriptionsExist,
88 addUserSubscription,
89 listUserSubscriptions,
90 getUserSubscription,
91 listUserSubscriptionVideos,
92 removeUserSubscription
93}
diff --git a/shared/extra-utils/users/users-command.ts b/shared/extra-utils/users/users-command.ts
new file mode 100644
index 000000000..d66ad15f2
--- /dev/null
+++ b/shared/extra-utils/users/users-command.ts
@@ -0,0 +1,414 @@
1import { omit, pick } from 'lodash'
2import {
3 HttpStatusCode,
4 MyUser,
5 ResultList,
6 User,
7 UserAdminFlag,
8 UserCreateResult,
9 UserRole,
10 UserUpdate,
11 UserUpdateMe,
12 UserVideoQuota,
13 UserVideoRate
14} from '@shared/models'
15import { ScopedToken } from '@shared/models/users/user-scoped-token'
16import { unwrapBody } from '../requests'
17import { AbstractCommand, OverrideCommandOptions } from '../shared'
18
19export class UsersCommand extends AbstractCommand {
20
21 askResetPassword (options: OverrideCommandOptions & {
22 email: string
23 }) {
24 const { email } = options
25 const path = '/api/v1/users/ask-reset-password'
26
27 return this.postBodyRequest({
28 ...options,
29
30 path,
31 fields: { email },
32 implicitToken: false,
33 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
34 })
35 }
36
37 resetPassword (options: OverrideCommandOptions & {
38 userId: number
39 verificationString: string
40 password: string
41 }) {
42 const { userId, verificationString, password } = options
43 const path = '/api/v1/users/' + userId + '/reset-password'
44
45 return this.postBodyRequest({
46 ...options,
47
48 path,
49 fields: { password, verificationString },
50 implicitToken: false,
51 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
52 })
53 }
54
55 // ---------------------------------------------------------------------------
56
57 askSendVerifyEmail (options: OverrideCommandOptions & {
58 email: string
59 }) {
60 const { email } = options
61 const path = '/api/v1/users/ask-send-verify-email'
62
63 return this.postBodyRequest({
64 ...options,
65
66 path,
67 fields: { email },
68 implicitToken: false,
69 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
70 })
71 }
72
73 verifyEmail (options: OverrideCommandOptions & {
74 userId: number
75 verificationString: string
76 isPendingEmail?: boolean // default false
77 }) {
78 const { userId, verificationString, isPendingEmail = false } = options
79 const path = '/api/v1/users/' + userId + '/verify-email'
80
81 return this.postBodyRequest({
82 ...options,
83
84 path,
85 fields: {
86 verificationString,
87 isPendingEmail
88 },
89 implicitToken: false,
90 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
91 })
92 }
93
94 // ---------------------------------------------------------------------------
95
96 banUser (options: OverrideCommandOptions & {
97 userId: number
98 reason?: string
99 }) {
100 const { userId, reason } = options
101 const path = '/api/v1/users' + '/' + userId + '/block'
102
103 return this.postBodyRequest({
104 ...options,
105
106 path,
107 fields: { reason },
108 implicitToken: true,
109 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
110 })
111 }
112
113 unbanUser (options: OverrideCommandOptions & {
114 userId: number
115 }) {
116 const { userId } = options
117 const path = '/api/v1/users' + '/' + userId + '/unblock'
118
119 return this.postBodyRequest({
120 ...options,
121
122 path,
123 implicitToken: true,
124 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
125 })
126 }
127
128 // ---------------------------------------------------------------------------
129
130 getMyScopedTokens (options: OverrideCommandOptions = {}) {
131 const path = '/api/v1/users/scoped-tokens'
132
133 return this.getRequestBody<ScopedToken>({
134 ...options,
135
136 path,
137 implicitToken: true,
138 defaultExpectedStatus: HttpStatusCode.OK_200
139 })
140 }
141
142 renewMyScopedTokens (options: OverrideCommandOptions = {}) {
143 const path = '/api/v1/users/scoped-tokens'
144
145 return this.postBodyRequest({
146 ...options,
147
148 path,
149 implicitToken: true,
150 defaultExpectedStatus: HttpStatusCode.OK_200
151 })
152 }
153
154 // ---------------------------------------------------------------------------
155
156 create (options: OverrideCommandOptions & {
157 username: string
158 password?: string
159 videoQuota?: number
160 videoQuotaDaily?: number
161 role?: UserRole
162 adminFlags?: UserAdminFlag
163 }) {
164 const {
165 username,
166 adminFlags,
167 password = 'password',
168 videoQuota = 42000000,
169 videoQuotaDaily = -1,
170 role = UserRole.USER
171 } = options
172
173 const path = '/api/v1/users'
174
175 return unwrapBody<{ user: UserCreateResult }>(this.postBodyRequest({
176 ...options,
177
178 path,
179 fields: {
180 username,
181 password,
182 role,
183 adminFlags,
184 email: username + '@example.com',
185 videoQuota,
186 videoQuotaDaily
187 },
188 implicitToken: true,
189 defaultExpectedStatus: HttpStatusCode.OK_200
190 })).then(res => res.user)
191 }
192
193 async generate (username: string) {
194 const password = 'password'
195 const user = await this.create({ username, password })
196
197 const token = await this.server.login.getAccessToken({ username, password })
198
199 const me = await this.getMyInfo({ token })
200
201 return {
202 token,
203 userId: user.id,
204 userChannelId: me.videoChannels[0].id
205 }
206 }
207
208 async generateUserAndToken (username: string) {
209 const password = 'password'
210 await this.create({ username, password })
211
212 return this.server.login.getAccessToken({ username, password })
213 }
214
215 register (options: OverrideCommandOptions & {
216 username: string
217 password?: string
218 displayName?: string
219 channel?: {
220 name: string
221 displayName: string
222 }
223 }) {
224 const { username, password = 'password', displayName, channel } = options
225 const path = '/api/v1/users/register'
226
227 return this.postBodyRequest({
228 ...options,
229
230 path,
231 fields: {
232 username,
233 password,
234 email: username + '@example.com',
235 displayName,
236 channel
237 },
238 implicitToken: false,
239 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
240 })
241 }
242
243 // ---------------------------------------------------------------------------
244
245 getMyInfo (options: OverrideCommandOptions = {}) {
246 const path = '/api/v1/users/me'
247
248 return this.getRequestBody<MyUser>({
249 ...options,
250
251 path,
252 implicitToken: true,
253 defaultExpectedStatus: HttpStatusCode.OK_200
254 })
255 }
256
257 getMyQuotaUsed (options: OverrideCommandOptions = {}) {
258 const path = '/api/v1/users/me/video-quota-used'
259
260 return this.getRequestBody<UserVideoQuota>({
261 ...options,
262
263 path,
264 implicitToken: true,
265 defaultExpectedStatus: HttpStatusCode.OK_200
266 })
267 }
268
269 getMyRating (options: OverrideCommandOptions & {
270 videoId: number | string
271 }) {
272 const { videoId } = options
273 const path = '/api/v1/users/me/videos/' + videoId + '/rating'
274
275 return this.getRequestBody<UserVideoRate>({
276 ...options,
277
278 path,
279 implicitToken: true,
280 defaultExpectedStatus: HttpStatusCode.OK_200
281 })
282 }
283
284 deleteMe (options: OverrideCommandOptions = {}) {
285 const path = '/api/v1/users/me'
286
287 return this.deleteRequest({
288 ...options,
289
290 path,
291 implicitToken: true,
292 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
293 })
294 }
295
296 updateMe (options: OverrideCommandOptions & UserUpdateMe) {
297 const path = '/api/v1/users/me'
298
299 const toSend: UserUpdateMe = omit(options, 'url', 'accessToken')
300
301 return this.putBodyRequest({
302 ...options,
303
304 path,
305 fields: toSend,
306 implicitToken: true,
307 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
308 })
309 }
310
311 updateMyAvatar (options: OverrideCommandOptions & {
312 fixture: string
313 }) {
314 const { fixture } = options
315 const path = '/api/v1/users/me/avatar/pick'
316
317 return this.updateImageRequest({
318 ...options,
319
320 path,
321 fixture,
322 fieldname: 'avatarfile',
323
324 implicitToken: true,
325 defaultExpectedStatus: HttpStatusCode.OK_200
326 })
327 }
328
329 // ---------------------------------------------------------------------------
330
331 get (options: OverrideCommandOptions & {
332 userId: number
333 withStats?: boolean // default false
334 }) {
335 const { userId, withStats } = options
336 const path = '/api/v1/users/' + userId
337
338 return this.getRequestBody<User>({
339 ...options,
340
341 path,
342 query: { withStats },
343 implicitToken: true,
344 defaultExpectedStatus: HttpStatusCode.OK_200
345 })
346 }
347
348 list (options: OverrideCommandOptions & {
349 start?: number
350 count?: number
351 sort?: string
352 search?: string
353 blocked?: boolean
354 } = {}) {
355 const path = '/api/v1/users'
356
357 return this.getRequestBody<ResultList<User>>({
358 ...options,
359
360 path,
361 query: pick(options, [ 'start', 'count', 'sort', 'search', 'blocked' ]),
362 implicitToken: true,
363 defaultExpectedStatus: HttpStatusCode.OK_200
364 })
365 }
366
367 remove (options: OverrideCommandOptions & {
368 userId: number
369 }) {
370 const { userId } = options
371 const path = '/api/v1/users/' + userId
372
373 return this.deleteRequest({
374 ...options,
375
376 path,
377 implicitToken: true,
378 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
379 })
380 }
381
382 update (options: OverrideCommandOptions & {
383 userId: number
384 email?: string
385 emailVerified?: boolean
386 videoQuota?: number
387 videoQuotaDaily?: number
388 password?: string
389 adminFlags?: UserAdminFlag
390 pluginAuth?: string
391 role?: UserRole
392 }) {
393 const path = '/api/v1/users/' + options.userId
394
395 const toSend: UserUpdate = {}
396 if (options.password !== undefined && options.password !== null) toSend.password = options.password
397 if (options.email !== undefined && options.email !== null) toSend.email = options.email
398 if (options.emailVerified !== undefined && options.emailVerified !== null) toSend.emailVerified = options.emailVerified
399 if (options.videoQuota !== undefined && options.videoQuota !== null) toSend.videoQuota = options.videoQuota
400 if (options.videoQuotaDaily !== undefined && options.videoQuotaDaily !== null) toSend.videoQuotaDaily = options.videoQuotaDaily
401 if (options.role !== undefined && options.role !== null) toSend.role = options.role
402 if (options.adminFlags !== undefined && options.adminFlags !== null) toSend.adminFlags = options.adminFlags
403 if (options.pluginAuth !== undefined) toSend.pluginAuth = options.pluginAuth
404
405 return this.putBodyRequest({
406 ...options,
407
408 path,
409 fields: toSend,
410 implicitToken: true,
411 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
412 })
413 }
414}
diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts
deleted file mode 100644
index 0f15962ad..000000000
--- a/shared/extra-utils/users/users.ts
+++ /dev/null
@@ -1,415 +0,0 @@
1import { omit } from 'lodash'
2import * as request from 'supertest'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
4import { UserUpdateMe } from '../../models/users'
5import { UserAdminFlag } from '../../models/users/user-flag.model'
6import { UserRegister } from '../../models/users/user-register.model'
7import { UserRole } from '../../models/users/user-role'
8import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateImageRequest } from '../requests/requests'
9import { ServerInfo } from '../server/servers'
10import { userLogin } from './login'
11
12function createUser (parameters: {
13 url: string
14 accessToken: string
15 username: string
16 password: string
17 videoQuota?: number
18 videoQuotaDaily?: number
19 role?: UserRole
20 adminFlags?: UserAdminFlag
21 specialStatus?: number
22}) {
23 const {
24 url,
25 accessToken,
26 username,
27 adminFlags,
28 password = 'password',
29 videoQuota = 1000000,
30 videoQuotaDaily = -1,
31 role = UserRole.USER,
32 specialStatus = HttpStatusCode.OK_200
33 } = parameters
34
35 const path = '/api/v1/users'
36 const body = {
37 username,
38 password,
39 role,
40 adminFlags,
41 email: username + '@example.com',
42 videoQuota,
43 videoQuotaDaily
44 }
45
46 return request(url)
47 .post(path)
48 .set('Accept', 'application/json')
49 .set('Authorization', 'Bearer ' + accessToken)
50 .send(body)
51 .expect(specialStatus)
52}
53
54async function generateUser (server: ServerInfo, username: string) {
55 const password = 'my super password'
56 const resCreate = await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
57
58 const token = await userLogin(server, { username, password })
59
60 const resMe = await getMyUserInformation(server.url, token)
61
62 return {
63 token,
64 userId: resCreate.body.user.id,
65 userChannelId: resMe.body.videoChannels[0].id
66 }
67}
68
69async function generateUserAccessToken (server: ServerInfo, username: string) {
70 const password = 'my super password'
71 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
72
73 return userLogin(server, { username, password })
74}
75
76function registerUser (url: string, username: string, password: string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
77 const path = '/api/v1/users/register'
78 const body = {
79 username,
80 password,
81 email: username + '@example.com'
82 }
83
84 return request(url)
85 .post(path)
86 .set('Accept', 'application/json')
87 .send(body)
88 .expect(specialStatus)
89}
90
91function registerUserWithChannel (options: {
92 url: string
93 user: { username: string, password: string, displayName?: string }
94 channel: { name: string, displayName: string }
95}) {
96 const path = '/api/v1/users/register'
97 const body: UserRegister = {
98 username: options.user.username,
99 password: options.user.password,
100 email: options.user.username + '@example.com',
101 channel: options.channel
102 }
103
104 if (options.user.displayName) {
105 Object.assign(body, { displayName: options.user.displayName })
106 }
107
108 return makePostBodyRequest({
109 url: options.url,
110 path,
111 fields: body,
112 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
113 })
114}
115
116function getMyUserInformation (url: string, accessToken: string, specialStatus = HttpStatusCode.OK_200) {
117 const path = '/api/v1/users/me'
118
119 return request(url)
120 .get(path)
121 .set('Accept', 'application/json')
122 .set('Authorization', 'Bearer ' + accessToken)
123 .expect(specialStatus)
124 .expect('Content-Type', /json/)
125}
126
127function getUserScopedTokens (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
128 const path = '/api/v1/users/scoped-tokens'
129
130 return makeGetRequest({
131 url,
132 path,
133 token,
134 statusCodeExpected
135 })
136}
137
138function renewUserScopedTokens (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) {
139 const path = '/api/v1/users/scoped-tokens'
140
141 return makePostBodyRequest({
142 url,
143 path,
144 token,
145 statusCodeExpected
146 })
147}
148
149function deleteMe (url: string, accessToken: string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
150 const path = '/api/v1/users/me'
151
152 return request(url)
153 .delete(path)
154 .set('Accept', 'application/json')
155 .set('Authorization', 'Bearer ' + accessToken)
156 .expect(specialStatus)
157}
158
159function getMyUserVideoQuotaUsed (url: string, accessToken: string, specialStatus = HttpStatusCode.OK_200) {
160 const path = '/api/v1/users/me/video-quota-used'
161
162 return request(url)
163 .get(path)
164 .set('Accept', 'application/json')
165 .set('Authorization', 'Bearer ' + accessToken)
166 .expect(specialStatus)
167 .expect('Content-Type', /json/)
168}
169
170function getUserInformation (url: string, accessToken: string, userId: number, withStats = false) {
171 const path = '/api/v1/users/' + userId
172
173 return request(url)
174 .get(path)
175 .query({ withStats })
176 .set('Accept', 'application/json')
177 .set('Authorization', 'Bearer ' + accessToken)
178 .expect(HttpStatusCode.OK_200)
179 .expect('Content-Type', /json/)
180}
181
182function getMyUserVideoRating (url: string, accessToken: string, videoId: number | string, specialStatus = HttpStatusCode.OK_200) {
183 const path = '/api/v1/users/me/videos/' + videoId + '/rating'
184
185 return request(url)
186 .get(path)
187 .set('Accept', 'application/json')
188 .set('Authorization', 'Bearer ' + accessToken)
189 .expect(specialStatus)
190 .expect('Content-Type', /json/)
191}
192
193function getUsersList (url: string, accessToken: string) {
194 const path = '/api/v1/users'
195
196 return request(url)
197 .get(path)
198 .set('Accept', 'application/json')
199 .set('Authorization', 'Bearer ' + accessToken)
200 .expect(HttpStatusCode.OK_200)
201 .expect('Content-Type', /json/)
202}
203
204function getUsersListPaginationAndSort (
205 url: string,
206 accessToken: string,
207 start: number,
208 count: number,
209 sort: string,
210 search?: string,
211 blocked?: boolean
212) {
213 const path = '/api/v1/users'
214
215 const query = {
216 start,
217 count,
218 sort,
219 search,
220 blocked
221 }
222
223 return request(url)
224 .get(path)
225 .query(query)
226 .set('Accept', 'application/json')
227 .set('Authorization', 'Bearer ' + accessToken)
228 .expect(HttpStatusCode.OK_200)
229 .expect('Content-Type', /json/)
230}
231
232function removeUser (url: string, userId: number | string, accessToken: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
233 const path = '/api/v1/users'
234
235 return request(url)
236 .delete(path + '/' + userId)
237 .set('Accept', 'application/json')
238 .set('Authorization', 'Bearer ' + accessToken)
239 .expect(expectedStatus)
240}
241
242function blockUser (
243 url: string,
244 userId: number | string,
245 accessToken: string,
246 expectedStatus = HttpStatusCode.NO_CONTENT_204,
247 reason?: string
248) {
249 const path = '/api/v1/users'
250 let body: any
251 if (reason) body = { reason }
252
253 return request(url)
254 .post(path + '/' + userId + '/block')
255 .send(body)
256 .set('Accept', 'application/json')
257 .set('Authorization', 'Bearer ' + accessToken)
258 .expect(expectedStatus)
259}
260
261function unblockUser (url: string, userId: number | string, accessToken: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
262 const path = '/api/v1/users'
263
264 return request(url)
265 .post(path + '/' + userId + '/unblock')
266 .set('Accept', 'application/json')
267 .set('Authorization', 'Bearer ' + accessToken)
268 .expect(expectedStatus)
269}
270
271function updateMyUser (options: { url: string, accessToken: string, statusCodeExpected?: HttpStatusCode } & UserUpdateMe) {
272 const path = '/api/v1/users/me'
273
274 const toSend: UserUpdateMe = omit(options, 'url', 'accessToken')
275
276 return makePutBodyRequest({
277 url: options.url,
278 path,
279 token: options.accessToken,
280 fields: toSend,
281 statusCodeExpected: options.statusCodeExpected || HttpStatusCode.NO_CONTENT_204
282 })
283}
284
285function updateMyAvatar (options: {
286 url: string
287 accessToken: string
288 fixture: string
289}) {
290 const path = '/api/v1/users/me/avatar/pick'
291
292 return updateImageRequest({ ...options, path, fieldname: 'avatarfile' })
293}
294
295function updateUser (options: {
296 url: string
297 userId: number
298 accessToken: string
299 email?: string
300 emailVerified?: boolean
301 videoQuota?: number
302 videoQuotaDaily?: number
303 password?: string
304 adminFlags?: UserAdminFlag
305 pluginAuth?: string
306 role?: UserRole
307}) {
308 const path = '/api/v1/users/' + options.userId
309
310 const toSend = {}
311 if (options.password !== undefined && options.password !== null) toSend['password'] = options.password
312 if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
313 if (options.emailVerified !== undefined && options.emailVerified !== null) toSend['emailVerified'] = options.emailVerified
314 if (options.videoQuota !== undefined && options.videoQuota !== null) toSend['videoQuota'] = options.videoQuota
315 if (options.videoQuotaDaily !== undefined && options.videoQuotaDaily !== null) toSend['videoQuotaDaily'] = options.videoQuotaDaily
316 if (options.role !== undefined && options.role !== null) toSend['role'] = options.role
317 if (options.adminFlags !== undefined && options.adminFlags !== null) toSend['adminFlags'] = options.adminFlags
318 if (options.pluginAuth !== undefined) toSend['pluginAuth'] = options.pluginAuth
319
320 return makePutBodyRequest({
321 url: options.url,
322 path,
323 token: options.accessToken,
324 fields: toSend,
325 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
326 })
327}
328
329function askResetPassword (url: string, email: string) {
330 const path = '/api/v1/users/ask-reset-password'
331
332 return makePostBodyRequest({
333 url,
334 path,
335 fields: { email },
336 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
337 })
338}
339
340function resetPassword (
341 url: string,
342 userId: number,
343 verificationString: string,
344 password: string,
345 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
346) {
347 const path = '/api/v1/users/' + userId + '/reset-password'
348
349 return makePostBodyRequest({
350 url,
351 path,
352 fields: { password, verificationString },
353 statusCodeExpected
354 })
355}
356
357function askSendVerifyEmail (url: string, email: string) {
358 const path = '/api/v1/users/ask-send-verify-email'
359
360 return makePostBodyRequest({
361 url,
362 path,
363 fields: { email },
364 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
365 })
366}
367
368function verifyEmail (
369 url: string,
370 userId: number,
371 verificationString: string,
372 isPendingEmail = false,
373 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
374) {
375 const path = '/api/v1/users/' + userId + '/verify-email'
376
377 return makePostBodyRequest({
378 url,
379 path,
380 fields: {
381 verificationString,
382 isPendingEmail
383 },
384 statusCodeExpected
385 })
386}
387
388// ---------------------------------------------------------------------------
389
390export {
391 createUser,
392 registerUser,
393 getMyUserInformation,
394 getMyUserVideoRating,
395 deleteMe,
396 registerUserWithChannel,
397 getMyUserVideoQuotaUsed,
398 getUsersList,
399 getUsersListPaginationAndSort,
400 removeUser,
401 updateUser,
402 updateMyUser,
403 getUserInformation,
404 blockUser,
405 unblockUser,
406 askResetPassword,
407 resetPassword,
408 renewUserScopedTokens,
409 updateMyAvatar,
410 generateUser,
411 askSendVerifyEmail,
412 generateUserAccessToken,
413 verifyEmail,
414 getUserScopedTokens
415}
diff --git a/shared/extra-utils/videos/blacklist-command.ts b/shared/extra-utils/videos/blacklist-command.ts
new file mode 100644
index 000000000..3a2ef89ba
--- /dev/null
+++ b/shared/extra-utils/videos/blacklist-command.ts
@@ -0,0 +1,76 @@
1
2import { HttpStatusCode, ResultList } from '@shared/models'
3import { VideoBlacklist, VideoBlacklistType } from '../../models/videos'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class BlacklistCommand extends AbstractCommand {
7
8 add (options: OverrideCommandOptions & {
9 videoId: number | string
10 reason?: string
11 unfederate?: boolean
12 }) {
13 const { videoId, reason, unfederate } = options
14 const path = '/api/v1/videos/' + videoId + '/blacklist'
15
16 return this.postBodyRequest({
17 ...options,
18
19 path,
20 fields: { reason, unfederate },
21 implicitToken: true,
22 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
23 })
24 }
25
26 update (options: OverrideCommandOptions & {
27 videoId: number | string
28 reason?: string
29 }) {
30 const { videoId, reason } = options
31 const path = '/api/v1/videos/' + videoId + '/blacklist'
32
33 return this.putBodyRequest({
34 ...options,
35
36 path,
37 fields: { reason },
38 implicitToken: true,
39 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
40 })
41 }
42
43 remove (options: OverrideCommandOptions & {
44 videoId: number | string
45 }) {
46 const { videoId } = options
47 const path = '/api/v1/videos/' + videoId + '/blacklist'
48
49 return this.deleteRequest({
50 ...options,
51
52 path,
53 implicitToken: true,
54 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
55 })
56 }
57
58 list (options: OverrideCommandOptions & {
59 sort?: string
60 type?: VideoBlacklistType
61 } = {}) {
62 const { sort, type } = options
63 const path = '/api/v1/videos/blacklist/'
64
65 const query = { sort, type }
66
67 return this.getRequestBody<ResultList<VideoBlacklist>>({
68 ...options,
69
70 path,
71 query,
72 implicitToken: true,
73 defaultExpectedStatus: HttpStatusCode.OK_200
74 })
75 }
76}
diff --git a/shared/extra-utils/videos/captions-command.ts b/shared/extra-utils/videos/captions-command.ts
new file mode 100644
index 000000000..a65ea99e3
--- /dev/null
+++ b/shared/extra-utils/videos/captions-command.ts
@@ -0,0 +1,65 @@
1import { HttpStatusCode, ResultList, VideoCaption } from '@shared/models'
2import { buildAbsoluteFixturePath } from '../miscs'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class CaptionsCommand extends AbstractCommand {
6
7 add (options: OverrideCommandOptions & {
8 videoId: string | number
9 language: string
10 fixture: string
11 mimeType?: string
12 }) {
13 const { videoId, language, fixture, mimeType } = options
14
15 const path = '/api/v1/videos/' + videoId + '/captions/' + language
16
17 const captionfile = buildAbsoluteFixturePath(fixture)
18 const captionfileAttach = mimeType
19 ? [ captionfile, { contentType: mimeType } ]
20 : captionfile
21
22 return this.putUploadRequest({
23 ...options,
24
25 path,
26 fields: {},
27 attaches: {
28 captionfile: captionfileAttach
29 },
30 implicitToken: true,
31 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
32 })
33 }
34
35 list (options: OverrideCommandOptions & {
36 videoId: string | number
37 }) {
38 const { videoId } = options
39 const path = '/api/v1/videos/' + videoId + '/captions'
40
41 return this.getRequestBody<ResultList<VideoCaption>>({
42 ...options,
43
44 path,
45 implicitToken: false,
46 defaultExpectedStatus: HttpStatusCode.OK_200
47 })
48 }
49
50 delete (options: OverrideCommandOptions & {
51 videoId: string | number
52 language: string
53 }) {
54 const { videoId, language } = options
55 const path = '/api/v1/videos/' + videoId + '/captions/' + language
56
57 return this.deleteRequest({
58 ...options,
59
60 path,
61 implicitToken: true,
62 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
63 })
64 }
65}
diff --git a/shared/extra-utils/videos/captions.ts b/shared/extra-utils/videos/captions.ts
new file mode 100644
index 000000000..ff8a43366
--- /dev/null
+++ b/shared/extra-utils/videos/captions.ts
@@ -0,0 +1,17 @@
1import { expect } from 'chai'
2import * as request from 'supertest'
3import { HttpStatusCode } from '@shared/models'
4
5async function testCaptionFile (url: string, captionPath: string, containsString: string) {
6 const res = await request(url)
7 .get(captionPath)
8 .expect(HttpStatusCode.OK_200)
9
10 expect(res.text).to.contain(containsString)
11}
12
13// ---------------------------------------------------------------------------
14
15export {
16 testCaptionFile
17}
diff --git a/shared/extra-utils/videos/change-ownership-command.ts b/shared/extra-utils/videos/change-ownership-command.ts
new file mode 100644
index 000000000..ad4c726ef
--- /dev/null
+++ b/shared/extra-utils/videos/change-ownership-command.ts
@@ -0,0 +1,68 @@
1
2import { HttpStatusCode, ResultList, VideoChangeOwnership } from '@shared/models'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class ChangeOwnershipCommand extends AbstractCommand {
6
7 create (options: OverrideCommandOptions & {
8 videoId: number | string
9 username: string
10 }) {
11 const { videoId, username } = options
12 const path = '/api/v1/videos/' + videoId + '/give-ownership'
13
14 return this.postBodyRequest({
15 ...options,
16
17 path,
18 fields: { username },
19 implicitToken: true,
20 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
21 })
22 }
23
24 list (options: OverrideCommandOptions = {}) {
25 const path = '/api/v1/videos/ownership'
26
27 return this.getRequestBody<ResultList<VideoChangeOwnership>>({
28 ...options,
29
30 path,
31 query: { sort: '-createdAt' },
32 implicitToken: true,
33 defaultExpectedStatus: HttpStatusCode.OK_200
34 })
35 }
36
37 accept (options: OverrideCommandOptions & {
38 ownershipId: number
39 channelId: number
40 }) {
41 const { ownershipId, channelId } = options
42 const path = '/api/v1/videos/ownership/' + ownershipId + '/accept'
43
44 return this.postBodyRequest({
45 ...options,
46
47 path,
48 fields: { channelId },
49 implicitToken: true,
50 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
51 })
52 }
53
54 refuse (options: OverrideCommandOptions & {
55 ownershipId: number
56 }) {
57 const { ownershipId } = options
58 const path = '/api/v1/videos/ownership/' + ownershipId + '/refuse'
59
60 return this.postBodyRequest({
61 ...options,
62
63 path,
64 implicitToken: true,
65 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
66 })
67 }
68}
diff --git a/shared/extra-utils/videos/channels-command.ts b/shared/extra-utils/videos/channels-command.ts
new file mode 100644
index 000000000..f8eb3f885
--- /dev/null
+++ b/shared/extra-utils/videos/channels-command.ts
@@ -0,0 +1,156 @@
1import { pick } from 'lodash'
2import { HttpStatusCode, ResultList, VideoChannel, VideoChannelCreateResult } from '@shared/models'
3import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model'
4import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model'
5import { unwrapBody } from '../requests'
6import { AbstractCommand, OverrideCommandOptions } from '../shared'
7
8export class ChannelsCommand extends AbstractCommand {
9
10 list (options: OverrideCommandOptions & {
11 start?: number
12 count?: number
13 sort?: string
14 withStats?: boolean
15 } = {}) {
16 const path = '/api/v1/video-channels'
17
18 return this.getRequestBody<ResultList<VideoChannel>>({
19 ...options,
20
21 path,
22 query: pick(options, [ 'start', 'count', 'sort', 'withStats' ]),
23 implicitToken: false,
24 defaultExpectedStatus: HttpStatusCode.OK_200
25 })
26 }
27
28 listByAccount (options: OverrideCommandOptions & {
29 accountName: string
30 start?: number
31 count?: number
32 sort?: string
33 withStats?: boolean
34 search?: string
35 }) {
36 const { accountName, sort = 'createdAt' } = options
37 const path = '/api/v1/accounts/' + accountName + '/video-channels'
38
39 return this.getRequestBody<ResultList<VideoChannel>>({
40 ...options,
41
42 path,
43 query: { sort, ...pick(options, [ 'start', 'count', 'withStats', 'search' ]) },
44 implicitToken: false,
45 defaultExpectedStatus: HttpStatusCode.OK_200
46 })
47 }
48
49 async create (options: OverrideCommandOptions & {
50 attributes: VideoChannelCreate
51 }) {
52 const path = '/api/v1/video-channels/'
53
54 // Default attributes
55 const defaultAttributes = {
56 displayName: 'my super video channel',
57 description: 'my super channel description',
58 support: 'my super channel support'
59 }
60 const attributes = { ...defaultAttributes, ...options.attributes }
61
62 const body = await unwrapBody<{ videoChannel: VideoChannelCreateResult }>(this.postBodyRequest({
63 ...options,
64
65 path,
66 fields: attributes,
67 implicitToken: true,
68 defaultExpectedStatus: HttpStatusCode.OK_200
69 }))
70
71 return body.videoChannel
72 }
73
74 update (options: OverrideCommandOptions & {
75 channelName: string
76 attributes: VideoChannelUpdate
77 }) {
78 const { channelName, attributes } = options
79 const path = '/api/v1/video-channels/' + channelName
80
81 return this.putBodyRequest({
82 ...options,
83
84 path,
85 fields: attributes,
86 implicitToken: true,
87 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
88 })
89 }
90
91 delete (options: OverrideCommandOptions & {
92 channelName: string
93 }) {
94 const path = '/api/v1/video-channels/' + options.channelName
95
96 return this.deleteRequest({
97 ...options,
98
99 path,
100 implicitToken: true,
101 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
102 })
103 }
104
105 get (options: OverrideCommandOptions & {
106 channelName: string
107 }) {
108 const path = '/api/v1/video-channels/' + options.channelName
109
110 return this.getRequestBody<VideoChannel>({
111 ...options,
112
113 path,
114 implicitToken: false,
115 defaultExpectedStatus: HttpStatusCode.OK_200
116 })
117 }
118
119 updateImage (options: OverrideCommandOptions & {
120 fixture: string
121 channelName: string | number
122 type: 'avatar' | 'banner'
123 }) {
124 const { channelName, fixture, type } = options
125
126 const path = `/api/v1/video-channels/${channelName}/${type}/pick`
127
128 return this.updateImageRequest({
129 ...options,
130
131 path,
132 fixture,
133 fieldname: type + 'file',
134
135 implicitToken: true,
136 defaultExpectedStatus: HttpStatusCode.OK_200
137 })
138 }
139
140 deleteImage (options: OverrideCommandOptions & {
141 channelName: string | number
142 type: 'avatar' | 'banner'
143 }) {
144 const { channelName, type } = options
145
146 const path = `/api/v1/video-channels/${channelName}/${type}`
147
148 return this.deleteRequest({
149 ...options,
150
151 path,
152 implicitToken: true,
153 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
154 })
155 }
156}
diff --git a/shared/extra-utils/videos/channels.ts b/shared/extra-utils/videos/channels.ts
new file mode 100644
index 000000000..756c47453
--- /dev/null
+++ b/shared/extra-utils/videos/channels.ts
@@ -0,0 +1,18 @@
1import { PeerTubeServer } from '../server/server'
2
3function setDefaultVideoChannel (servers: PeerTubeServer[]) {
4 const tasks: Promise<any>[] = []
5
6 for (const server of servers) {
7 const p = server.users.getMyInfo()
8 .then(user => { server.store.channel = user.videoChannels[0] })
9
10 tasks.push(p)
11 }
12
13 return Promise.all(tasks)
14}
15
16export {
17 setDefaultVideoChannel
18}
diff --git a/shared/extra-utils/videos/comments-command.ts b/shared/extra-utils/videos/comments-command.ts
new file mode 100644
index 000000000..f0d163a07
--- /dev/null
+++ b/shared/extra-utils/videos/comments-command.ts
@@ -0,0 +1,152 @@
1import { pick } from 'lodash'
2import { HttpStatusCode, ResultList, VideoComment, VideoCommentThreads, VideoCommentThreadTree } from '@shared/models'
3import { unwrapBody } from '../requests'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class CommentsCommand extends AbstractCommand {
7
8 private lastVideoId: number | string
9 private lastThreadId: number
10 private lastReplyId: number
11
12 listForAdmin (options: OverrideCommandOptions & {
13 start?: number
14 count?: number
15 sort?: string
16 isLocal?: boolean
17 search?: string
18 searchAccount?: string
19 searchVideo?: string
20 } = {}) {
21 const { sort = '-createdAt' } = options
22 const path = '/api/v1/videos/comments'
23
24 const query = { sort, ...pick(options, [ 'start', 'count', 'isLocal', 'search', 'searchAccount', 'searchVideo' ]) }
25
26 return this.getRequestBody<ResultList<VideoComment>>({
27 ...options,
28
29 path,
30 query,
31 implicitToken: true,
32 defaultExpectedStatus: HttpStatusCode.OK_200
33 })
34 }
35
36 listThreads (options: OverrideCommandOptions & {
37 videoId: number | string
38 start?: number
39 count?: number
40 sort?: string
41 }) {
42 const { start, count, sort, videoId } = options
43 const path = '/api/v1/videos/' + videoId + '/comment-threads'
44
45 return this.getRequestBody<VideoCommentThreads>({
46 ...options,
47
48 path,
49 query: { start, count, sort },
50 implicitToken: false,
51 defaultExpectedStatus: HttpStatusCode.OK_200
52 })
53 }
54
55 getThread (options: OverrideCommandOptions & {
56 videoId: number | string
57 threadId: number
58 }) {
59 const { videoId, threadId } = options
60 const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
61
62 return this.getRequestBody<VideoCommentThreadTree>({
63 ...options,
64
65 path,
66 implicitToken: false,
67 defaultExpectedStatus: HttpStatusCode.OK_200
68 })
69 }
70
71 async createThread (options: OverrideCommandOptions & {
72 videoId: number | string
73 text: string
74 }) {
75 const { videoId, text } = options
76 const path = '/api/v1/videos/' + videoId + '/comment-threads'
77
78 const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({
79 ...options,
80
81 path,
82 fields: { text },
83 implicitToken: true,
84 defaultExpectedStatus: HttpStatusCode.OK_200
85 }))
86
87 this.lastThreadId = body.comment?.id
88 this.lastVideoId = videoId
89
90 return body.comment
91 }
92
93 async addReply (options: OverrideCommandOptions & {
94 videoId: number | string
95 toCommentId: number
96 text: string
97 }) {
98 const { videoId, toCommentId, text } = options
99 const path = '/api/v1/videos/' + videoId + '/comments/' + toCommentId
100
101 const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({
102 ...options,
103
104 path,
105 fields: { text },
106 implicitToken: true,
107 defaultExpectedStatus: HttpStatusCode.OK_200
108 }))
109
110 this.lastReplyId = body.comment?.id
111
112 return body.comment
113 }
114
115 async addReplyToLastReply (options: OverrideCommandOptions & {
116 text: string
117 }) {
118 return this.addReply({ ...options, videoId: this.lastVideoId, toCommentId: this.lastReplyId })
119 }
120
121 async addReplyToLastThread (options: OverrideCommandOptions & {
122 text: string
123 }) {
124 return this.addReply({ ...options, videoId: this.lastVideoId, toCommentId: this.lastThreadId })
125 }
126
127 async findCommentId (options: OverrideCommandOptions & {
128 videoId: number | string
129 text: string
130 }) {
131 const { videoId, text } = options
132 const { data } = await this.listThreads({ videoId, count: 25, sort: '-createdAt' })
133
134 return data.find(c => c.text === text).id
135 }
136
137 delete (options: OverrideCommandOptions & {
138 videoId: number | string
139 commentId: number
140 }) {
141 const { videoId, commentId } = options
142 const path = '/api/v1/videos/' + videoId + '/comments/' + commentId
143
144 return this.deleteRequest({
145 ...options,
146
147 path,
148 implicitToken: true,
149 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
150 })
151 }
152}
diff --git a/shared/extra-utils/videos/history-command.ts b/shared/extra-utils/videos/history-command.ts
new file mode 100644
index 000000000..13b7150c1
--- /dev/null
+++ b/shared/extra-utils/videos/history-command.ts
@@ -0,0 +1,58 @@
1import { HttpStatusCode, ResultList, Video } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class HistoryCommand extends AbstractCommand {
5
6 wathVideo (options: OverrideCommandOptions & {
7 videoId: number | string
8 currentTime: number
9 }) {
10 const { videoId, currentTime } = options
11
12 const path = '/api/v1/videos/' + videoId + '/watching'
13 const fields = { currentTime }
14
15 return this.putBodyRequest({
16 ...options,
17
18 path,
19 fields,
20 implicitToken: true,
21 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
22 })
23 }
24
25 list (options: OverrideCommandOptions & {
26 search?: string
27 } = {}) {
28 const { search } = options
29 const path = '/api/v1/users/me/history/videos'
30
31 return this.getRequestBody<ResultList<Video>>({
32 ...options,
33
34 path,
35 query: {
36 search
37 },
38 implicitToken: true,
39 defaultExpectedStatus: HttpStatusCode.OK_200
40 })
41 }
42
43 remove (options: OverrideCommandOptions & {
44 beforeDate?: string
45 } = {}) {
46 const { beforeDate } = options
47 const path = '/api/v1/users/me/history/videos/remove'
48
49 return this.postBodyRequest({
50 ...options,
51
52 path,
53 fields: { beforeDate },
54 implicitToken: true,
55 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
56 })
57 }
58}
diff --git a/shared/extra-utils/videos/imports-command.ts b/shared/extra-utils/videos/imports-command.ts
new file mode 100644
index 000000000..e4944694d
--- /dev/null
+++ b/shared/extra-utils/videos/imports-command.ts
@@ -0,0 +1,47 @@
1
2import { HttpStatusCode, ResultList } from '@shared/models'
3import { VideoImport, VideoImportCreate } from '../../models/videos'
4import { unwrapBody } from '../requests'
5import { AbstractCommand, OverrideCommandOptions } from '../shared'
6
7export class ImportsCommand extends AbstractCommand {
8
9 importVideo (options: OverrideCommandOptions & {
10 attributes: VideoImportCreate & { torrentfile?: string }
11 }) {
12 const { attributes } = options
13 const path = '/api/v1/videos/imports'
14
15 let attaches: any = {}
16 if (attributes.torrentfile) attaches = { torrentfile: attributes.torrentfile }
17
18 return unwrapBody<VideoImport>(this.postUploadRequest({
19 ...options,
20
21 path,
22 attaches,
23 fields: options.attributes,
24 implicitToken: true,
25 defaultExpectedStatus: HttpStatusCode.OK_200
26 }))
27 }
28
29 getMyVideoImports (options: OverrideCommandOptions & {
30 sort?: string
31 } = {}) {
32 const { sort } = options
33 const path = '/api/v1/users/me/videos/imports'
34
35 const query = {}
36 if (sort) query['sort'] = sort
37
38 return this.getRequestBody<ResultList<VideoImport>>({
39 ...options,
40
41 path,
42 query: { sort },
43 implicitToken: true,
44 defaultExpectedStatus: HttpStatusCode.OK_200
45 })
46 }
47}
diff --git a/shared/extra-utils/videos/index.ts b/shared/extra-utils/videos/index.ts
new file mode 100644
index 000000000..26e663f46
--- /dev/null
+++ b/shared/extra-utils/videos/index.ts
@@ -0,0 +1,19 @@
1export * from './blacklist-command'
2export * from './captions-command'
3export * from './captions'
4export * from './change-ownership-command'
5export * from './channels'
6export * from './channels-command'
7export * from './comments-command'
8export * from './history-command'
9export * from './imports-command'
10export * from './live-command'
11export * from './live'
12export * from './playlists-command'
13export * from './playlists'
14export * from './services-command'
15export * from './streaming-playlists-command'
16export * from './streaming-playlists'
17export * from './comments-command'
18export * from './videos-command'
19export * from './videos'
diff --git a/shared/extra-utils/videos/live-command.ts b/shared/extra-utils/videos/live-command.ts
new file mode 100644
index 000000000..bf9486a05
--- /dev/null
+++ b/shared/extra-utils/videos/live-command.ts
@@ -0,0 +1,154 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { readdir } from 'fs-extra'
4import { omit } from 'lodash'
5import { join } from 'path'
6import { HttpStatusCode, LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models'
7import { wait } from '../miscs'
8import { unwrapBody } from '../requests'
9import { AbstractCommand, OverrideCommandOptions } from '../shared'
10import { sendRTMPStream, testFfmpegStreamError } from './live'
11
12export class LiveCommand extends AbstractCommand {
13
14 get (options: OverrideCommandOptions & {
15 videoId: number | string
16 }) {
17 const path = '/api/v1/videos/live'
18
19 return this.getRequestBody<LiveVideo>({
20 ...options,
21
22 path: path + '/' + options.videoId,
23 implicitToken: true,
24 defaultExpectedStatus: HttpStatusCode.OK_200
25 })
26 }
27
28 update (options: OverrideCommandOptions & {
29 videoId: number | string
30 fields: LiveVideoUpdate
31 }) {
32 const { videoId, fields } = options
33 const path = '/api/v1/videos/live'
34
35 return this.putBodyRequest({
36 ...options,
37
38 path: path + '/' + videoId,
39 fields,
40 implicitToken: true,
41 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
42 })
43 }
44
45 async create (options: OverrideCommandOptions & {
46 fields: LiveVideoCreate
47 }) {
48 const { fields } = options
49 const path = '/api/v1/videos/live'
50
51 const attaches: any = {}
52 if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
53 if (fields.previewfile) attaches.previewfile = fields.previewfile
54
55 const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({
56 ...options,
57
58 path,
59 attaches,
60 fields: omit(fields, 'thumbnailfile', 'previewfile'),
61 implicitToken: true,
62 defaultExpectedStatus: HttpStatusCode.OK_200
63 }))
64
65 return body.video
66 }
67
68 async sendRTMPStreamInVideo (options: OverrideCommandOptions & {
69 videoId: number | string
70 fixtureName?: string
71 }) {
72 const { videoId, fixtureName } = options
73 const videoLive = await this.get({ videoId })
74
75 return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName)
76 }
77
78 async runAndTestStreamError (options: OverrideCommandOptions & {
79 videoId: number | string
80 shouldHaveError: boolean
81 }) {
82 const command = await this.sendRTMPStreamInVideo(options)
83
84 return testFfmpegStreamError(command, options.shouldHaveError)
85 }
86
87 waitUntilPublished (options: OverrideCommandOptions & {
88 videoId: number | string
89 }) {
90 const { videoId } = options
91 return this.waitUntilState({ videoId, state: VideoState.PUBLISHED })
92 }
93
94 waitUntilWaiting (options: OverrideCommandOptions & {
95 videoId: number | string
96 }) {
97 const { videoId } = options
98 return this.waitUntilState({ videoId, state: VideoState.WAITING_FOR_LIVE })
99 }
100
101 waitUntilEnded (options: OverrideCommandOptions & {
102 videoId: number | string
103 }) {
104 const { videoId } = options
105 return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED })
106 }
107
108 waitUntilSegmentGeneration (options: OverrideCommandOptions & {
109 videoUUID: string
110 resolution: number
111 segment: number
112 }) {
113 const { resolution, segment, videoUUID } = options
114 const segmentName = `${resolution}-00000${segment}.ts`
115
116 return this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, 2, false)
117 }
118
119 async waitUntilSaved (options: OverrideCommandOptions & {
120 videoId: number | string
121 }) {
122 let video: VideoDetails
123
124 do {
125 video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
126
127 await wait(500)
128 } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED)
129 }
130
131 async countPlaylists (options: OverrideCommandOptions & {
132 videoUUID: string
133 }) {
134 const basePath = this.server.servers.buildDirectory('streaming-playlists')
135 const hlsPath = join(basePath, 'hls', options.videoUUID)
136
137 const files = await readdir(hlsPath)
138
139 return files.filter(f => f.endsWith('.m3u8')).length
140 }
141
142 private async waitUntilState (options: OverrideCommandOptions & {
143 videoId: number | string
144 state: VideoState
145 }) {
146 let video: VideoDetails
147
148 do {
149 video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
150
151 await wait(500)
152 } while (video.state.id !== options.state)
153 }
154}
diff --git a/shared/extra-utils/videos/live.ts b/shared/extra-utils/videos/live.ts
index c0384769b..502964b1a 100644
--- a/shared/extra-utils/videos/live.ts
+++ b/shared/extra-utils/videos/live.ts
@@ -3,69 +3,9 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import * as ffmpeg from 'fluent-ffmpeg' 4import * as ffmpeg from 'fluent-ffmpeg'
5import { pathExists, readdir } from 'fs-extra' 5import { pathExists, readdir } from 'fs-extra'
6import { omit } from 'lodash'
7import { join } from 'path' 6import { join } from 'path'
8import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoDetails, VideoState } from '@shared/models' 7import { buildAbsoluteFixturePath, wait } from '../miscs'
9import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 8import { PeerTubeServer } from '../server/server'
10import { buildAbsoluteFixturePath, buildServerDirectory, wait } from '../miscs/miscs'
11import { makeGetRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests'
12import { ServerInfo, waitUntilLog } from '../server/servers'
13import { getVideoWithToken } from './videos'
14
15function getLive (url: string, token: string, videoId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
16 const path = '/api/v1/videos/live'
17
18 return makeGetRequest({
19 url,
20 token,
21 path: path + '/' + videoId,
22 statusCodeExpected
23 })
24}
25
26function updateLive (
27 url: string,
28 token: string,
29 videoId: number | string,
30 fields: LiveVideoUpdate,
31 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
32) {
33 const path = '/api/v1/videos/live'
34
35 return makePutBodyRequest({
36 url,
37 token,
38 path: path + '/' + videoId,
39 fields,
40 statusCodeExpected
41 })
42}
43
44function createLive (url: string, token: string, fields: LiveVideoCreate, statusCodeExpected = HttpStatusCode.OK_200) {
45 const path = '/api/v1/videos/live'
46
47 const attaches: any = {}
48 if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
49 if (fields.previewfile) attaches.previewfile = fields.previewfile
50
51 const updatedFields = omit(fields, 'thumbnailfile', 'previewfile')
52
53 return makeUploadRequest({
54 url,
55 path,
56 token,
57 attaches,
58 fields: updatedFields,
59 statusCodeExpected
60 })
61}
62
63async function sendRTMPStreamInVideo (url: string, token: string, videoId: number | string, fixtureName?: string) {
64 const res = await getLive(url, token, videoId)
65 const videoLive = res.body as LiveVideo
66
67 return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName)
68}
69 9
70function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') { 10function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') {
71 const fixture = buildAbsoluteFixturePath(fixtureName) 11 const fixture = buildAbsoluteFixturePath(fixtureName)
@@ -109,12 +49,6 @@ function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 1
109 }) 49 })
110} 50}
111 51
112async function runAndTestFfmpegStreamError (url: string, token: string, videoId: number | string, shouldHaveError: boolean) {
113 const command = await sendRTMPStreamInVideo(url, token, videoId)
114
115 return testFfmpegStreamError(command, shouldHaveError)
116}
117
118async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) { 52async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) {
119 let error: Error 53 let error: Error
120 54
@@ -136,53 +70,14 @@ async function stopFfmpeg (command: ffmpeg.FfmpegCommand) {
136 await wait(500) 70 await wait(500)
137} 71}
138 72
139function waitUntilLivePublished (url: string, token: string, videoId: number | string) { 73async function waitUntilLivePublishedOnAllServers (servers: PeerTubeServer[], videoId: string) {
140 return waitUntilLiveState(url, token, videoId, VideoState.PUBLISHED)
141}
142
143function waitUntilLiveWaiting (url: string, token: string, videoId: number | string) {
144 return waitUntilLiveState(url, token, videoId, VideoState.WAITING_FOR_LIVE)
145}
146
147function waitUntilLiveEnded (url: string, token: string, videoId: number | string) {
148 return waitUntilLiveState(url, token, videoId, VideoState.LIVE_ENDED)
149}
150
151function waitUntilLiveSegmentGeneration (server: ServerInfo, videoUUID: string, resolutionNum: number, segmentNum: number) {
152 const segmentName = `${resolutionNum}-00000${segmentNum}.ts`
153 return waitUntilLog(server, `${videoUUID}/${segmentName}`, 2, false)
154}
155
156async function waitUntilLiveState (url: string, token: string, videoId: number | string, state: VideoState) {
157 let video: VideoDetails
158
159 do {
160 const res = await getVideoWithToken(url, token, videoId)
161 video = res.body
162
163 await wait(500)
164 } while (video.state.id !== state)
165}
166
167async function waitUntilLiveSaved (url: string, token: string, videoId: number | string) {
168 let video: VideoDetails
169
170 do {
171 const res = await getVideoWithToken(url, token, videoId)
172 video = res.body
173
174 await wait(500)
175 } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED)
176}
177
178async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoId: string) {
179 for (const server of servers) { 74 for (const server of servers) {
180 await waitUntilLivePublished(server.url, server.accessToken, videoId) 75 await server.live.waitUntilPublished({ videoId })
181 } 76 }
182} 77}
183 78
184async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resolutions: number[] = []) { 79async function checkLiveCleanup (server: PeerTubeServer, videoUUID: string, resolutions: number[] = []) {
185 const basePath = buildServerDirectory(server, 'streaming-playlists') 80 const basePath = server.servers.buildDirectory('streaming-playlists')
186 const hlsPath = join(basePath, 'hls', videoUUID) 81 const hlsPath = join(basePath, 'hls', videoUUID)
187 82
188 if (resolutions.length === 0) { 83 if (resolutions.length === 0) {
@@ -206,33 +101,11 @@ async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resoluti
206 expect(files).to.contain('segments-sha256.json') 101 expect(files).to.contain('segments-sha256.json')
207} 102}
208 103
209async function getPlaylistsCount (server: ServerInfo, videoUUID: string) {
210 const basePath = buildServerDirectory(server, 'streaming-playlists')
211 const hlsPath = join(basePath, 'hls', videoUUID)
212
213 const files = await readdir(hlsPath)
214
215 return files.filter(f => f.endsWith('.m3u8')).length
216}
217
218// ---------------------------------------------------------------------------
219
220export { 104export {
221 getLive, 105 sendRTMPStream,
222 getPlaylistsCount,
223 waitUntilLiveSaved,
224 waitUntilLivePublished,
225 updateLive,
226 createLive,
227 runAndTestFfmpegStreamError,
228 checkLiveCleanup,
229 waitUntilLiveSegmentGeneration,
230 stopFfmpeg,
231 waitUntilLiveWaiting,
232 sendRTMPStreamInVideo,
233 waitUntilLiveEnded,
234 waitFfmpegUntilError, 106 waitFfmpegUntilError,
107 testFfmpegStreamError,
108 stopFfmpeg,
235 waitUntilLivePublishedOnAllServers, 109 waitUntilLivePublishedOnAllServers,
236 sendRTMPStream, 110 checkLiveCleanup
237 testFfmpegStreamError
238} 111}
diff --git a/shared/extra-utils/videos/playlists-command.ts b/shared/extra-utils/videos/playlists-command.ts
new file mode 100644
index 000000000..6f329800e
--- /dev/null
+++ b/shared/extra-utils/videos/playlists-command.ts
@@ -0,0 +1,279 @@
1import { omit, pick } from 'lodash'
2import {
3 BooleanBothQuery,
4 HttpStatusCode,
5 ResultList,
6 VideoExistInPlaylist,
7 VideoPlaylist,
8 VideoPlaylistCreate,
9 VideoPlaylistCreateResult,
10 VideoPlaylistElement,
11 VideoPlaylistElementCreate,
12 VideoPlaylistElementCreateResult,
13 VideoPlaylistElementUpdate,
14 VideoPlaylistReorder,
15 VideoPlaylistType,
16 VideoPlaylistUpdate
17} from '@shared/models'
18import { unwrapBody } from '../requests'
19import { AbstractCommand, OverrideCommandOptions } from '../shared'
20
21export class PlaylistsCommand extends AbstractCommand {
22
23 list (options: OverrideCommandOptions & {
24 start?: number
25 count?: number
26 sort?: string
27 }) {
28 const path = '/api/v1/video-playlists'
29 const query = pick(options, [ 'start', 'count', 'sort' ])
30
31 return this.getRequestBody<ResultList<VideoPlaylist>>({
32 ...options,
33
34 path,
35 query,
36 implicitToken: false,
37 defaultExpectedStatus: HttpStatusCode.OK_200
38 })
39 }
40
41 listByChannel (options: OverrideCommandOptions & {
42 handle: string
43 start?: number
44 count?: number
45 sort?: string
46 }) {
47 const path = '/api/v1/video-channels/' + options.handle + '/video-playlists'
48 const query = pick(options, [ 'start', 'count', 'sort' ])
49
50 return this.getRequestBody<ResultList<VideoPlaylist>>({
51 ...options,
52
53 path,
54 query,
55 implicitToken: false,
56 defaultExpectedStatus: HttpStatusCode.OK_200
57 })
58 }
59
60 listByAccount (options: OverrideCommandOptions & {
61 handle: string
62 start?: number
63 count?: number
64 sort?: string
65 search?: string
66 playlistType?: VideoPlaylistType
67 }) {
68 const path = '/api/v1/accounts/' + options.handle + '/video-playlists'
69 const query = pick(options, [ 'start', 'count', 'sort', 'search', 'playlistType' ])
70
71 return this.getRequestBody<ResultList<VideoPlaylist>>({
72 ...options,
73
74 path,
75 query,
76 implicitToken: false,
77 defaultExpectedStatus: HttpStatusCode.OK_200
78 })
79 }
80
81 get (options: OverrideCommandOptions & {
82 playlistId: number | string
83 }) {
84 const { playlistId } = options
85 const path = '/api/v1/video-playlists/' + playlistId
86
87 return this.getRequestBody<VideoPlaylist>({
88 ...options,
89
90 path,
91 implicitToken: false,
92 defaultExpectedStatus: HttpStatusCode.OK_200
93 })
94 }
95
96 listVideos (options: OverrideCommandOptions & {
97 playlistId: number | string
98 start?: number
99 count?: number
100 query?: { nsfw?: BooleanBothQuery }
101 }) {
102 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
103 const query = options.query ?? {}
104
105 return this.getRequestBody<ResultList<VideoPlaylistElement>>({
106 ...options,
107
108 path,
109 query: {
110 ...query,
111 start: options.start,
112 count: options.count
113 },
114 implicitToken: true,
115 defaultExpectedStatus: HttpStatusCode.OK_200
116 })
117 }
118
119 delete (options: OverrideCommandOptions & {
120 playlistId: number | string
121 }) {
122 const path = '/api/v1/video-playlists/' + options.playlistId
123
124 return this.deleteRequest({
125 ...options,
126
127 path,
128 implicitToken: true,
129 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
130 })
131 }
132
133 async create (options: OverrideCommandOptions & {
134 attributes: VideoPlaylistCreate
135 }) {
136 const path = '/api/v1/video-playlists'
137
138 const fields = omit(options.attributes, 'thumbnailfile')
139
140 const attaches = options.attributes.thumbnailfile
141 ? { thumbnailfile: options.attributes.thumbnailfile }
142 : {}
143
144 const body = await unwrapBody<{ videoPlaylist: VideoPlaylistCreateResult }>(this.postUploadRequest({
145 ...options,
146
147 path,
148 fields,
149 attaches,
150 implicitToken: true,
151 defaultExpectedStatus: HttpStatusCode.OK_200
152 }))
153
154 return body.videoPlaylist
155 }
156
157 update (options: OverrideCommandOptions & {
158 attributes: VideoPlaylistUpdate
159 playlistId: number | string
160 }) {
161 const path = '/api/v1/video-playlists/' + options.playlistId
162
163 const fields = omit(options.attributes, 'thumbnailfile')
164
165 const attaches = options.attributes.thumbnailfile
166 ? { thumbnailfile: options.attributes.thumbnailfile }
167 : {}
168
169 return this.putUploadRequest({
170 ...options,
171
172 path,
173 fields,
174 attaches,
175 implicitToken: true,
176 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
177 })
178 }
179
180 async addElement (options: OverrideCommandOptions & {
181 playlistId: number | string
182 attributes: VideoPlaylistElementCreate | { videoId: string }
183 }) {
184 const attributes = {
185 ...options.attributes,
186
187 videoId: await this.server.videos.getId({ ...options, uuid: options.attributes.videoId })
188 }
189
190 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
191
192 const body = await unwrapBody<{ videoPlaylistElement: VideoPlaylistElementCreateResult }>(this.postBodyRequest({
193 ...options,
194
195 path,
196 fields: attributes,
197 implicitToken: true,
198 defaultExpectedStatus: HttpStatusCode.OK_200
199 }))
200
201 return body.videoPlaylistElement
202 }
203
204 updateElement (options: OverrideCommandOptions & {
205 playlistId: number | string
206 elementId: number | string
207 attributes: VideoPlaylistElementUpdate
208 }) {
209 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.elementId
210
211 return this.putBodyRequest({
212 ...options,
213
214 path,
215 fields: options.attributes,
216 implicitToken: true,
217 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
218 })
219 }
220
221 removeElement (options: OverrideCommandOptions & {
222 playlistId: number | string
223 elementId: number
224 }) {
225 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.elementId
226
227 return this.deleteRequest({
228 ...options,
229
230 path,
231 implicitToken: true,
232 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
233 })
234 }
235
236 reorderElements (options: OverrideCommandOptions & {
237 playlistId: number | string
238 attributes: VideoPlaylistReorder
239 }) {
240 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
241
242 return this.postBodyRequest({
243 ...options,
244
245 path,
246 fields: options.attributes,
247 implicitToken: true,
248 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
249 })
250 }
251
252 getPrivacies (options: OverrideCommandOptions = {}) {
253 const path = '/api/v1/video-playlists/privacies'
254
255 return this.getRequestBody<{ [ id: number ]: string }>({
256 ...options,
257
258 path,
259 implicitToken: false,
260 defaultExpectedStatus: HttpStatusCode.OK_200
261 })
262 }
263
264 videosExist (options: OverrideCommandOptions & {
265 videoIds: number[]
266 }) {
267 const { videoIds } = options
268 const path = '/api/v1/users/me/video-playlists/videos-exist'
269
270 return this.getRequestBody<VideoExistInPlaylist>({
271 ...options,
272
273 path,
274 query: { videoIds },
275 implicitToken: true,
276 defaultExpectedStatus: HttpStatusCode.OK_200
277 })
278 }
279}
diff --git a/shared/extra-utils/videos/playlists.ts b/shared/extra-utils/videos/playlists.ts
new file mode 100644
index 000000000..3dde52bb9
--- /dev/null
+++ b/shared/extra-utils/videos/playlists.ts
@@ -0,0 +1,25 @@
1import { expect } from 'chai'
2import { readdir } from 'fs-extra'
3import { join } from 'path'
4import { root } from '../miscs'
5
6async function checkPlaylistFilesWereRemoved (
7 playlistUUID: string,
8 internalServerNumber: number,
9 directories = [ 'thumbnails' ]
10) {
11 const testDirectory = 'test' + internalServerNumber
12
13 for (const directory of directories) {
14 const directoryPath = join(root(), testDirectory, directory)
15
16 const files = await readdir(directoryPath)
17 for (const file of files) {
18 expect(file).to.not.contain(playlistUUID)
19 }
20 }
21}
22
23export {
24 checkPlaylistFilesWereRemoved
25}
diff --git a/shared/extra-utils/videos/services-command.ts b/shared/extra-utils/videos/services-command.ts
new file mode 100644
index 000000000..06760df42
--- /dev/null
+++ b/shared/extra-utils/videos/services-command.ts
@@ -0,0 +1,29 @@
1import { HttpStatusCode } from '@shared/models'
2import { AbstractCommand, OverrideCommandOptions } from '../shared'
3
4export class ServicesCommand extends AbstractCommand {
5
6 getOEmbed (options: OverrideCommandOptions & {
7 oembedUrl: string
8 format?: string
9 maxHeight?: number
10 maxWidth?: number
11 }) {
12 const path = '/services/oembed'
13 const query = {
14 url: options.oembedUrl,
15 format: options.format,
16 maxheight: options.maxHeight,
17 maxwidth: options.maxWidth
18 }
19
20 return this.getRequest({
21 ...options,
22
23 path,
24 query,
25 implicitToken: false,
26 defaultExpectedStatus: HttpStatusCode.OK_200
27 })
28 }
29}
diff --git a/shared/extra-utils/videos/services.ts b/shared/extra-utils/videos/services.ts
deleted file mode 100644
index e13a788bd..000000000
--- a/shared/extra-utils/videos/services.ts
+++ /dev/null
@@ -1,24 +0,0 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function getOEmbed (url: string, oembedUrl: string, format?: string, maxHeight?: number, maxWidth?: number) {
5 const path = '/services/oembed'
6 const query = {
7 url: oembedUrl,
8 format,
9 maxheight: maxHeight,
10 maxwidth: maxWidth
11 }
12
13 return request(url)
14 .get(path)
15 .query(query)
16 .set('Accept', 'application/json')
17 .expect(HttpStatusCode.OK_200)
18}
19
20// ---------------------------------------------------------------------------
21
22export {
23 getOEmbed
24}
diff --git a/shared/extra-utils/videos/streaming-playlists-command.ts b/shared/extra-utils/videos/streaming-playlists-command.ts
new file mode 100644
index 000000000..9662685da
--- /dev/null
+++ b/shared/extra-utils/videos/streaming-playlists-command.ts
@@ -0,0 +1,44 @@
1import { HttpStatusCode } from '@shared/models'
2import { unwrapBody, unwrapText } from '../requests'
3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4
5export class StreamingPlaylistsCommand extends AbstractCommand {
6
7 get (options: OverrideCommandOptions & {
8 url: string
9 }) {
10 return unwrapText(this.getRawRequest({
11 ...options,
12
13 url: options.url,
14 implicitToken: false,
15 defaultExpectedStatus: HttpStatusCode.OK_200
16 }))
17 }
18
19 getSegment (options: OverrideCommandOptions & {
20 url: string
21 range?: string
22 }) {
23 return unwrapBody<Buffer>(this.getRawRequest({
24 ...options,
25
26 url: options.url,
27 range: options.range,
28 implicitToken: false,
29 defaultExpectedStatus: HttpStatusCode.OK_200
30 }))
31 }
32
33 getSegmentSha256 (options: OverrideCommandOptions & {
34 url: string
35 }) {
36 return unwrapBody<{ [ id: string ]: string }>(this.getRawRequest({
37 ...options,
38
39 url: options.url,
40 implicitToken: false,
41 defaultExpectedStatus: HttpStatusCode.OK_200
42 }))
43 }
44}
diff --git a/shared/extra-utils/videos/streaming-playlists.ts b/shared/extra-utils/videos/streaming-playlists.ts
new file mode 100644
index 000000000..1ae3fefc1
--- /dev/null
+++ b/shared/extra-utils/videos/streaming-playlists.ts
@@ -0,0 +1,75 @@
1import { expect } from 'chai'
2import { sha256 } from '@server/helpers/core-utils'
3import { HttpStatusCode, VideoStreamingPlaylist } from '@shared/models'
4import { PeerTubeServer } from '../server'
5
6async function checkSegmentHash (options: {
7 server: PeerTubeServer
8 baseUrlPlaylist: string
9 baseUrlSegment: string
10 videoUUID: string
11 resolution: number
12 hlsPlaylist: VideoStreamingPlaylist
13}) {
14 const { server, baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist } = options
15 const command = server.streamingPlaylists
16
17 const playlist = await command.get({ url: `${baseUrlPlaylist}/${videoUUID}/${resolution}.m3u8` })
18
19 const videoName = `${videoUUID}-${resolution}-fragmented.mp4`
20
21 const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
22
23 const length = parseInt(matches[1], 10)
24 const offset = parseInt(matches[2], 10)
25 const range = `${offset}-${offset + length - 1}`
26
27 const segmentBody = await command.getSegment({
28 url: `${baseUrlSegment}/${videoUUID}/${videoName}`,
29 expectedStatus: HttpStatusCode.PARTIAL_CONTENT_206,
30 range: `bytes=${range}`
31 })
32
33 const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url })
34 expect(sha256(segmentBody)).to.equal(shaBody[videoName][range])
35}
36
37async function checkLiveSegmentHash (options: {
38 server: PeerTubeServer
39 baseUrlSegment: string
40 videoUUID: string
41 segmentName: string
42 hlsPlaylist: VideoStreamingPlaylist
43}) {
44 const { server, baseUrlSegment, videoUUID, segmentName, hlsPlaylist } = options
45 const command = server.streamingPlaylists
46
47 const segmentBody = await command.getSegment({ url: `${baseUrlSegment}/${videoUUID}/${segmentName}` })
48 const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url })
49
50 expect(sha256(segmentBody)).to.equal(shaBody[segmentName])
51}
52
53async function checkResolutionsInMasterPlaylist (options: {
54 server: PeerTubeServer
55 playlistUrl: string
56 resolutions: number[]
57}) {
58 const { server, playlistUrl, resolutions } = options
59
60 const masterPlaylist = await server.streamingPlaylists.get({ url: playlistUrl })
61
62 for (const resolution of resolutions) {
63 const reg = new RegExp(
64 '#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"'
65 )
66
67 expect(masterPlaylist).to.match(reg)
68 }
69}
70
71export {
72 checkSegmentHash,
73 checkLiveSegmentHash,
74 checkResolutionsInMasterPlaylist
75}
diff --git a/shared/extra-utils/videos/video-blacklist.ts b/shared/extra-utils/videos/video-blacklist.ts
deleted file mode 100644
index aa1548537..000000000
--- a/shared/extra-utils/videos/video-blacklist.ts
+++ /dev/null
@@ -1,79 +0,0 @@
1import * as request from 'supertest'
2import { VideoBlacklistType } from '../../models/videos'
3import { makeGetRequest } from '..'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5
6function addVideoToBlacklist (
7 url: string,
8 token: string,
9 videoId: number | string,
10 reason?: string,
11 unfederate?: boolean,
12 specialStatus = HttpStatusCode.NO_CONTENT_204
13) {
14 const path = '/api/v1/videos/' + videoId + '/blacklist'
15
16 return request(url)
17 .post(path)
18 .send({ reason, unfederate })
19 .set('Accept', 'application/json')
20 .set('Authorization', 'Bearer ' + token)
21 .expect(specialStatus)
22}
23
24function updateVideoBlacklist (
25 url: string,
26 token: string,
27 videoId: number,
28 reason?: string,
29 specialStatus = HttpStatusCode.NO_CONTENT_204
30) {
31 const path = '/api/v1/videos/' + videoId + '/blacklist'
32
33 return request(url)
34 .put(path)
35 .send({ reason })
36 .set('Accept', 'application/json')
37 .set('Authorization', 'Bearer ' + token)
38 .expect(specialStatus)
39}
40
41function removeVideoFromBlacklist (url: string, token: string, videoId: number | string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
42 const path = '/api/v1/videos/' + videoId + '/blacklist'
43
44 return request(url)
45 .delete(path)
46 .set('Accept', 'application/json')
47 .set('Authorization', 'Bearer ' + token)
48 .expect(specialStatus)
49}
50
51function getBlacklistedVideosList (parameters: {
52 url: string
53 token: string
54 sort?: string
55 type?: VideoBlacklistType
56 specialStatus?: HttpStatusCode
57}) {
58 const { url, token, sort, type, specialStatus = HttpStatusCode.OK_200 } = parameters
59 const path = '/api/v1/videos/blacklist/'
60
61 const query = { sort, type }
62
63 return makeGetRequest({
64 url,
65 path,
66 query,
67 token,
68 statusCodeExpected: specialStatus
69 })
70}
71
72// ---------------------------------------------------------------------------
73
74export {
75 addVideoToBlacklist,
76 removeVideoFromBlacklist,
77 getBlacklistedVideosList,
78 updateVideoBlacklist
79}
diff --git a/shared/extra-utils/videos/video-captions.ts b/shared/extra-utils/videos/video-captions.ts
deleted file mode 100644
index 62eec7b90..000000000
--- a/shared/extra-utils/videos/video-captions.ts
+++ /dev/null
@@ -1,72 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makeUploadRequest } from '../requests/requests'
2import * as request from 'supertest'
3import * as chai from 'chai'
4import { buildAbsoluteFixturePath } from '../miscs/miscs'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6
7const expect = chai.expect
8
9function createVideoCaption (args: {
10 url: string
11 accessToken: string
12 videoId: string | number
13 language: string
14 fixture: string
15 mimeType?: string
16 statusCodeExpected?: number
17}) {
18 const path = '/api/v1/videos/' + args.videoId + '/captions/' + args.language
19
20 const captionfile = buildAbsoluteFixturePath(args.fixture)
21 const captionfileAttach = args.mimeType ? [ captionfile, { contentType: args.mimeType } ] : captionfile
22
23 return makeUploadRequest({
24 method: 'PUT',
25 url: args.url,
26 path,
27 token: args.accessToken,
28 fields: {},
29 attaches: {
30 captionfile: captionfileAttach
31 },
32 statusCodeExpected: args.statusCodeExpected || HttpStatusCode.NO_CONTENT_204
33 })
34}
35
36function listVideoCaptions (url: string, videoId: string | number) {
37 const path = '/api/v1/videos/' + videoId + '/captions'
38
39 return makeGetRequest({
40 url,
41 path,
42 statusCodeExpected: HttpStatusCode.OK_200
43 })
44}
45
46function deleteVideoCaption (url: string, token: string, videoId: string | number, language: string) {
47 const path = '/api/v1/videos/' + videoId + '/captions/' + language
48
49 return makeDeleteRequest({
50 url,
51 token,
52 path,
53 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
54 })
55}
56
57async function testCaptionFile (url: string, captionPath: string, containsString: string) {
58 const res = await request(url)
59 .get(captionPath)
60 .expect(HttpStatusCode.OK_200)
61
62 expect(res.text).to.contain(containsString)
63}
64
65// ---------------------------------------------------------------------------
66
67export {
68 createVideoCaption,
69 listVideoCaptions,
70 testCaptionFile,
71 deleteVideoCaption
72}
diff --git a/shared/extra-utils/videos/video-change-ownership.ts b/shared/extra-utils/videos/video-change-ownership.ts
deleted file mode 100644
index ef82a7636..000000000
--- a/shared/extra-utils/videos/video-change-ownership.ts
+++ /dev/null
@@ -1,72 +0,0 @@
1import * as request from 'supertest'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function changeVideoOwnership (
5 url: string,
6 token: string,
7 videoId: number | string,
8 username,
9 expectedStatus = HttpStatusCode.NO_CONTENT_204
10) {
11 const path = '/api/v1/videos/' + videoId + '/give-ownership'
12
13 return request(url)
14 .post(path)
15 .set('Accept', 'application/json')
16 .set('Authorization', 'Bearer ' + token)
17 .send({ username })
18 .expect(expectedStatus)
19}
20
21function getVideoChangeOwnershipList (url: string, token: string) {
22 const path = '/api/v1/videos/ownership'
23
24 return request(url)
25 .get(path)
26 .query({ sort: '-createdAt' })
27 .set('Accept', 'application/json')
28 .set('Authorization', 'Bearer ' + token)
29 .expect(HttpStatusCode.OK_200)
30 .expect('Content-Type', /json/)
31}
32
33function acceptChangeOwnership (
34 url: string,
35 token: string,
36 ownershipId: string,
37 channelId: number,
38 expectedStatus = HttpStatusCode.NO_CONTENT_204
39) {
40 const path = '/api/v1/videos/ownership/' + ownershipId + '/accept'
41
42 return request(url)
43 .post(path)
44 .set('Accept', 'application/json')
45 .set('Authorization', 'Bearer ' + token)
46 .send({ channelId })
47 .expect(expectedStatus)
48}
49
50function refuseChangeOwnership (
51 url: string,
52 token: string,
53 ownershipId: string,
54 expectedStatus = HttpStatusCode.NO_CONTENT_204
55) {
56 const path = '/api/v1/videos/ownership/' + ownershipId + '/refuse'
57
58 return request(url)
59 .post(path)
60 .set('Accept', 'application/json')
61 .set('Authorization', 'Bearer ' + token)
62 .expect(expectedStatus)
63}
64
65// ---------------------------------------------------------------------------
66
67export {
68 changeVideoOwnership,
69 getVideoChangeOwnershipList,
70 acceptChangeOwnership,
71 refuseChangeOwnership
72}
diff --git a/shared/extra-utils/videos/video-channels.ts b/shared/extra-utils/videos/video-channels.ts
deleted file mode 100644
index 0aab93e52..000000000
--- a/shared/extra-utils/videos/video-channels.ts
+++ /dev/null
@@ -1,192 +0,0 @@
1/* eslint-disable @typescript-eslint/no-floating-promises */
2
3import * as request from 'supertest'
4import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model'
5import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model'
6import { makeDeleteRequest, makeGetRequest, updateImageRequest } from '../requests/requests'
7import { ServerInfo } from '../server/servers'
8import { MyUser, User } from '../../models/users/user.model'
9import { getMyUserInformation } from '../users/users'
10import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
11
12function getVideoChannelsList (url: string, start: number, count: number, sort?: string, withStats?: boolean) {
13 const path = '/api/v1/video-channels'
14
15 const req = request(url)
16 .get(path)
17 .query({ start: start })
18 .query({ count: count })
19
20 if (sort) req.query({ sort })
21 if (withStats) req.query({ withStats })
22
23 return req.set('Accept', 'application/json')
24 .expect(HttpStatusCode.OK_200)
25 .expect('Content-Type', /json/)
26}
27
28function getAccountVideoChannelsList (parameters: {
29 url: string
30 accountName: string
31 start?: number
32 count?: number
33 sort?: string
34 specialStatus?: HttpStatusCode
35 withStats?: boolean
36 search?: string
37}) {
38 const {
39 url,
40 accountName,
41 start,
42 count,
43 sort = 'createdAt',
44 specialStatus = HttpStatusCode.OK_200,
45 withStats = false,
46 search
47 } = parameters
48
49 const path = '/api/v1/accounts/' + accountName + '/video-channels'
50
51 return makeGetRequest({
52 url,
53 path,
54 query: {
55 start,
56 count,
57 sort,
58 withStats,
59 search
60 },
61 statusCodeExpected: specialStatus
62 })
63}
64
65function addVideoChannel (
66 url: string,
67 token: string,
68 videoChannelAttributesArg: VideoChannelCreate,
69 expectedStatus = HttpStatusCode.OK_200
70) {
71 const path = '/api/v1/video-channels/'
72
73 // Default attributes
74 let attributes = {
75 displayName: 'my super video channel',
76 description: 'my super channel description',
77 support: 'my super channel support'
78 }
79 attributes = Object.assign(attributes, videoChannelAttributesArg)
80
81 return request(url)
82 .post(path)
83 .send(attributes)
84 .set('Accept', 'application/json')
85 .set('Authorization', 'Bearer ' + token)
86 .expect(expectedStatus)
87}
88
89function updateVideoChannel (
90 url: string,
91 token: string,
92 channelName: string,
93 attributes: VideoChannelUpdate,
94 expectedStatus = HttpStatusCode.NO_CONTENT_204
95) {
96 const body: any = {}
97 const path = '/api/v1/video-channels/' + channelName
98
99 if (attributes.displayName) body.displayName = attributes.displayName
100 if (attributes.description) body.description = attributes.description
101 if (attributes.support) body.support = attributes.support
102 if (attributes.bulkVideosSupportUpdate) body.bulkVideosSupportUpdate = attributes.bulkVideosSupportUpdate
103
104 return request(url)
105 .put(path)
106 .send(body)
107 .set('Accept', 'application/json')
108 .set('Authorization', 'Bearer ' + token)
109 .expect(expectedStatus)
110}
111
112function deleteVideoChannel (url: string, token: string, channelName: string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
113 const path = '/api/v1/video-channels/' + channelName
114
115 return request(url)
116 .delete(path)
117 .set('Accept', 'application/json')
118 .set('Authorization', 'Bearer ' + token)
119 .expect(expectedStatus)
120}
121
122function getVideoChannel (url: string, channelName: string) {
123 const path = '/api/v1/video-channels/' + channelName
124
125 return request(url)
126 .get(path)
127 .set('Accept', 'application/json')
128 .expect(HttpStatusCode.OK_200)
129 .expect('Content-Type', /json/)
130}
131
132function updateVideoChannelImage (options: {
133 url: string
134 accessToken: string
135 fixture: string
136 videoChannelName: string | number
137 type: 'avatar' | 'banner'
138}) {
139 const path = `/api/v1/video-channels/${options.videoChannelName}/${options.type}/pick`
140
141 return updateImageRequest({ ...options, path, fieldname: options.type + 'file' })
142}
143
144function deleteVideoChannelImage (options: {
145 url: string
146 accessToken: string
147 videoChannelName: string | number
148 type: 'avatar' | 'banner'
149}) {
150 const path = `/api/v1/video-channels/${options.videoChannelName}/${options.type}`
151
152 return makeDeleteRequest({
153 url: options.url,
154 token: options.accessToken,
155 path,
156 statusCodeExpected: 204
157 })
158}
159
160function setDefaultVideoChannel (servers: ServerInfo[]) {
161 const tasks: Promise<any>[] = []
162
163 for (const server of servers) {
164 const p = getMyUserInformation(server.url, server.accessToken)
165 .then(res => { server.videoChannel = (res.body as User).videoChannels[0] })
166
167 tasks.push(p)
168 }
169
170 return Promise.all(tasks)
171}
172
173async function getDefaultVideoChannel (url: string, token: string) {
174 const res = await getMyUserInformation(url, token)
175
176 return (res.body as MyUser).videoChannels[0].id
177}
178
179// ---------------------------------------------------------------------------
180
181export {
182 updateVideoChannelImage,
183 getVideoChannelsList,
184 getAccountVideoChannelsList,
185 addVideoChannel,
186 updateVideoChannel,
187 deleteVideoChannel,
188 getVideoChannel,
189 setDefaultVideoChannel,
190 deleteVideoChannelImage,
191 getDefaultVideoChannel
192}
diff --git a/shared/extra-utils/videos/video-comments.ts b/shared/extra-utils/videos/video-comments.ts
deleted file mode 100644
index 71b9f875a..000000000
--- a/shared/extra-utils/videos/video-comments.ts
+++ /dev/null
@@ -1,138 +0,0 @@
1/* eslint-disable @typescript-eslint/no-floating-promises */
2
3import * as request from 'supertest'
4import { makeDeleteRequest, makeGetRequest } from '../requests/requests'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6
7function getAdminVideoComments (options: {
8 url: string
9 token: string
10 start: number
11 count: number
12 sort?: string
13 isLocal?: boolean
14 search?: string
15 searchAccount?: string
16 searchVideo?: string
17}) {
18 const { url, token, start, count, sort, isLocal, search, searchAccount, searchVideo } = options
19 const path = '/api/v1/videos/comments'
20
21 const query = {
22 start,
23 count,
24 sort: sort || '-createdAt'
25 }
26
27 if (isLocal !== undefined) Object.assign(query, { isLocal })
28 if (search !== undefined) Object.assign(query, { search })
29 if (searchAccount !== undefined) Object.assign(query, { searchAccount })
30 if (searchVideo !== undefined) Object.assign(query, { searchVideo })
31
32 return makeGetRequest({
33 url,
34 path,
35 token,
36 query,
37 statusCodeExpected: HttpStatusCode.OK_200
38 })
39}
40
41function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string, token?: string) {
42 const path = '/api/v1/videos/' + videoId + '/comment-threads'
43
44 const req = request(url)
45 .get(path)
46 .query({ start: start })
47 .query({ count: count })
48
49 if (sort) req.query({ sort })
50 if (token) req.set('Authorization', 'Bearer ' + token)
51
52 return req.set('Accept', 'application/json')
53 .expect(HttpStatusCode.OK_200)
54 .expect('Content-Type', /json/)
55}
56
57function getVideoThreadComments (url: string, videoId: number | string, threadId: number, token?: string) {
58 const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
59
60 const req = request(url)
61 .get(path)
62 .set('Accept', 'application/json')
63
64 if (token) req.set('Authorization', 'Bearer ' + token)
65
66 return req.expect(HttpStatusCode.OK_200)
67 .expect('Content-Type', /json/)
68}
69
70function addVideoCommentThread (
71 url: string,
72 token: string,
73 videoId: number | string,
74 text: string,
75 expectedStatus = HttpStatusCode.OK_200
76) {
77 const path = '/api/v1/videos/' + videoId + '/comment-threads'
78
79 return request(url)
80 .post(path)
81 .send({ text })
82 .set('Accept', 'application/json')
83 .set('Authorization', 'Bearer ' + token)
84 .expect(expectedStatus)
85}
86
87function addVideoCommentReply (
88 url: string,
89 token: string,
90 videoId: number | string,
91 inReplyToCommentId: number,
92 text: string,
93 expectedStatus = HttpStatusCode.OK_200
94) {
95 const path = '/api/v1/videos/' + videoId + '/comments/' + inReplyToCommentId
96
97 return request(url)
98 .post(path)
99 .send({ text })
100 .set('Accept', 'application/json')
101 .set('Authorization', 'Bearer ' + token)
102 .expect(expectedStatus)
103}
104
105async function findCommentId (url: string, videoId: number | string, text: string) {
106 const res = await getVideoCommentThreads(url, videoId, 0, 25, '-createdAt')
107
108 return res.body.data.find(c => c.text === text).id as number
109}
110
111function deleteVideoComment (
112 url: string,
113 token: string,
114 videoId: number | string,
115 commentId: number,
116 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
117) {
118 const path = '/api/v1/videos/' + videoId + '/comments/' + commentId
119
120 return makeDeleteRequest({
121 url,
122 path,
123 token,
124 statusCodeExpected
125 })
126}
127
128// ---------------------------------------------------------------------------
129
130export {
131 getVideoCommentThreads,
132 getAdminVideoComments,
133 getVideoThreadComments,
134 addVideoCommentThread,
135 addVideoCommentReply,
136 findCommentId,
137 deleteVideoComment
138}
diff --git a/shared/extra-utils/videos/video-history.ts b/shared/extra-utils/videos/video-history.ts
deleted file mode 100644
index b989e14dc..000000000
--- a/shared/extra-utils/videos/video-history.ts
+++ /dev/null
@@ -1,49 +0,0 @@
1import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
2import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
3
4function userWatchVideo (
5 url: string,
6 token: string,
7 videoId: number | string,
8 currentTime: number,
9 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
10) {
11 const path = '/api/v1/videos/' + videoId + '/watching'
12 const fields = { currentTime }
13
14 return makePutBodyRequest({ url, path, token, fields, statusCodeExpected })
15}
16
17function listMyVideosHistory (url: string, token: string, search?: string) {
18 const path = '/api/v1/users/me/history/videos'
19
20 return makeGetRequest({
21 url,
22 path,
23 token,
24 query: {
25 search
26 },
27 statusCodeExpected: HttpStatusCode.OK_200
28 })
29}
30
31function removeMyVideosHistory (url: string, token: string, beforeDate?: string) {
32 const path = '/api/v1/users/me/history/videos/remove'
33
34 return makePostBodyRequest({
35 url,
36 path,
37 token,
38 fields: beforeDate ? { beforeDate } : {},
39 statusCodeExpected: HttpStatusCode.NO_CONTENT_204
40 })
41}
42
43// ---------------------------------------------------------------------------
44
45export {
46 userWatchVideo,
47 listMyVideosHistory,
48 removeMyVideosHistory
49}
diff --git a/shared/extra-utils/videos/video-imports.ts b/shared/extra-utils/videos/video-imports.ts
deleted file mode 100644
index 81c0163cb..000000000
--- a/shared/extra-utils/videos/video-imports.ts
+++ /dev/null
@@ -1,90 +0,0 @@
1
2import { VideoImportCreate } from '../../models/videos'
3import { makeGetRequest, makeUploadRequest } from '../requests/requests'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5
6function getYoutubeVideoUrl () {
7 return 'https://www.youtube.com/watch?v=msX3jv1XdvM'
8}
9
10function getYoutubeHDRVideoUrl () {
11 /**
12 * The video is used to check format-selection correctness wrt. HDR,
13 * which brings its own set of oddities outside of a MediaSource.
14 * FIXME: refactor once HDR is supported at playback
15 *
16 * The video needs to have the following format_ids:
17 * (which you can check by using `youtube-dl <url> -F`):
18 * - 303 (1080p webm vp9)
19 * - 299 (1080p mp4 avc1)
20 * - 335 (1080p webm vp9.2 HDR)
21 *
22 * 15 jan. 2021: TEST VIDEO NOT CURRENTLY PROVIDING
23 * - 400 (1080p mp4 av01)
24 * - 315 (2160p webm vp9 HDR)
25 * - 337 (2160p webm vp9.2 HDR)
26 * - 401 (2160p mp4 av01 HDR)
27 */
28 return 'https://www.youtube.com/watch?v=qR5vOXbZsI4'
29}
30
31function getMagnetURI () {
32 // eslint-disable-next-line max-len
33 return 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4'
34}
35
36function getBadVideoUrl () {
37 return 'https://download.cpy.re/peertube/bad_video.mp4'
38}
39
40function getGoodVideoUrl () {
41 return 'https://download.cpy.re/peertube/good_video.mp4'
42}
43
44function importVideo (
45 url: string,
46 token: string,
47 attributes: VideoImportCreate & { torrentfile?: string },
48 statusCodeExpected = HttpStatusCode.OK_200
49) {
50 const path = '/api/v1/videos/imports'
51
52 let attaches: any = {}
53 if (attributes.torrentfile) attaches = { torrentfile: attributes.torrentfile }
54
55 return makeUploadRequest({
56 url,
57 path,
58 token,
59 attaches,
60 fields: attributes,
61 statusCodeExpected
62 })
63}
64
65function getMyVideoImports (url: string, token: string, sort?: string) {
66 const path = '/api/v1/users/me/videos/imports'
67
68 const query = {}
69 if (sort) query['sort'] = sort
70
71 return makeGetRequest({
72 url,
73 query,
74 path,
75 token,
76 statusCodeExpected: HttpStatusCode.OK_200
77 })
78}
79
80// ---------------------------------------------------------------------------
81
82export {
83 getBadVideoUrl,
84 getYoutubeVideoUrl,
85 getYoutubeHDRVideoUrl,
86 importVideo,
87 getMagnetURI,
88 getMyVideoImports,
89 getGoodVideoUrl
90}
diff --git a/shared/extra-utils/videos/video-playlists.ts b/shared/extra-utils/videos/video-playlists.ts
deleted file mode 100644
index c6f799e5d..000000000
--- a/shared/extra-utils/videos/video-playlists.ts
+++ /dev/null
@@ -1,320 +0,0 @@
1import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests'
2import { VideoPlaylistCreate } from '../../models/videos/playlist/video-playlist-create.model'
3import { omit } from 'lodash'
4import { VideoPlaylistUpdate } from '../../models/videos/playlist/video-playlist-update.model'
5import { VideoPlaylistElementCreate } from '../../models/videos/playlist/video-playlist-element-create.model'
6import { VideoPlaylistElementUpdate } from '../../models/videos/playlist/video-playlist-element-update.model'
7import { videoUUIDToId } from './videos'
8import { join } from 'path'
9import { root } from '..'
10import { readdir } from 'fs-extra'
11import { expect } from 'chai'
12import { VideoPlaylistType } from '../../models/videos/playlist/video-playlist-type.model'
13import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
14
15function getVideoPlaylistsList (url: string, start: number, count: number, sort?: string) {
16 const path = '/api/v1/video-playlists'
17
18 const query = {
19 start,
20 count,
21 sort
22 }
23
24 return makeGetRequest({
25 url,
26 path,
27 query,
28 statusCodeExpected: HttpStatusCode.OK_200
29 })
30}
31
32function getVideoChannelPlaylistsList (url: string, videoChannelName: string, start: number, count: number, sort?: string) {
33 const path = '/api/v1/video-channels/' + videoChannelName + '/video-playlists'
34
35 const query = {
36 start,
37 count,
38 sort
39 }
40
41 return makeGetRequest({
42 url,
43 path,
44 query,
45 statusCodeExpected: HttpStatusCode.OK_200
46 })
47}
48
49function getAccountPlaylistsList (url: string, accountName: string, start: number, count: number, sort?: string, search?: string) {
50 const path = '/api/v1/accounts/' + accountName + '/video-playlists'
51
52 const query = {
53 start,
54 count,
55 sort,
56 search
57 }
58
59 return makeGetRequest({
60 url,
61 path,
62 query,
63 statusCodeExpected: HttpStatusCode.OK_200
64 })
65}
66
67function getAccountPlaylistsListWithToken (
68 url: string,
69 token: string,
70 accountName: string,
71 start: number,
72 count: number,
73 playlistType?: VideoPlaylistType,
74 sort?: string
75) {
76 const path = '/api/v1/accounts/' + accountName + '/video-playlists'
77
78 const query = {
79 start,
80 count,
81 playlistType,
82 sort
83 }
84
85 return makeGetRequest({
86 url,
87 token,
88 path,
89 query,
90 statusCodeExpected: HttpStatusCode.OK_200
91 })
92}
93
94function getVideoPlaylist (url: string, playlistId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
95 const path = '/api/v1/video-playlists/' + playlistId
96
97 return makeGetRequest({
98 url,
99 path,
100 statusCodeExpected
101 })
102}
103
104function getVideoPlaylistWithToken (url: string, token: string, playlistId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
105 const path = '/api/v1/video-playlists/' + playlistId
106
107 return makeGetRequest({
108 url,
109 token,
110 path,
111 statusCodeExpected
112 })
113}
114
115function deleteVideoPlaylist (url: string, token: string, playlistId: number | string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
116 const path = '/api/v1/video-playlists/' + playlistId
117
118 return makeDeleteRequest({
119 url,
120 path,
121 token,
122 statusCodeExpected
123 })
124}
125
126function createVideoPlaylist (options: {
127 url: string
128 token: string
129 playlistAttrs: VideoPlaylistCreate
130 expectedStatus?: number
131}) {
132 const path = '/api/v1/video-playlists'
133
134 const fields = omit(options.playlistAttrs, 'thumbnailfile')
135
136 const attaches = options.playlistAttrs.thumbnailfile
137 ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
138 : {}
139
140 return makeUploadRequest({
141 method: 'POST',
142 url: options.url,
143 path,
144 token: options.token,
145 fields,
146 attaches,
147 statusCodeExpected: options.expectedStatus || HttpStatusCode.OK_200
148 })
149}
150
151function updateVideoPlaylist (options: {
152 url: string
153 token: string
154 playlistAttrs: VideoPlaylistUpdate
155 playlistId: number | string
156 expectedStatus?: number
157}) {
158 const path = '/api/v1/video-playlists/' + options.playlistId
159
160 const fields = omit(options.playlistAttrs, 'thumbnailfile')
161
162 const attaches = options.playlistAttrs.thumbnailfile
163 ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
164 : {}
165
166 return makeUploadRequest({
167 method: 'PUT',
168 url: options.url,
169 path,
170 token: options.token,
171 fields,
172 attaches,
173 statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
174 })
175}
176
177async function addVideoInPlaylist (options: {
178 url: string
179 token: string
180 playlistId: number | string
181 elementAttrs: VideoPlaylistElementCreate | { videoId: string }
182 expectedStatus?: number
183}) {
184 options.elementAttrs.videoId = await videoUUIDToId(options.url, options.elementAttrs.videoId)
185
186 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
187
188 return makePostBodyRequest({
189 url: options.url,
190 path,
191 token: options.token,
192 fields: options.elementAttrs,
193 statusCodeExpected: options.expectedStatus || HttpStatusCode.OK_200
194 })
195}
196
197function updateVideoPlaylistElement (options: {
198 url: string
199 token: string
200 playlistId: number | string
201 playlistElementId: number | string
202 elementAttrs: VideoPlaylistElementUpdate
203 expectedStatus?: number
204}) {
205 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.playlistElementId
206
207 return makePutBodyRequest({
208 url: options.url,
209 path,
210 token: options.token,
211 fields: options.elementAttrs,
212 statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
213 })
214}
215
216function removeVideoFromPlaylist (options: {
217 url: string
218 token: string
219 playlistId: number | string
220 playlistElementId: number
221 expectedStatus?: number
222}) {
223 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.playlistElementId
224
225 return makeDeleteRequest({
226 url: options.url,
227 path,
228 token: options.token,
229 statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
230 })
231}
232
233function reorderVideosPlaylist (options: {
234 url: string
235 token: string
236 playlistId: number | string
237 elementAttrs: {
238 startPosition: number
239 insertAfterPosition: number
240 reorderLength?: number
241 }
242 expectedStatus?: number
243}) {
244 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
245
246 return makePostBodyRequest({
247 url: options.url,
248 path,
249 token: options.token,
250 fields: options.elementAttrs,
251 statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
252 })
253}
254
255async function checkPlaylistFilesWereRemoved (
256 playlistUUID: string,
257 internalServerNumber: number,
258 directories = [ 'thumbnails' ]
259) {
260 const testDirectory = 'test' + internalServerNumber
261
262 for (const directory of directories) {
263 const directoryPath = join(root(), testDirectory, directory)
264
265 const files = await readdir(directoryPath)
266 for (const file of files) {
267 expect(file).to.not.contain(playlistUUID)
268 }
269 }
270}
271
272function getVideoPlaylistPrivacies (url: string) {
273 const path = '/api/v1/video-playlists/privacies'
274
275 return makeGetRequest({
276 url,
277 path,
278 statusCodeExpected: HttpStatusCode.OK_200
279 })
280}
281
282function doVideosExistInMyPlaylist (url: string, token: string, videoIds: number[]) {
283 const path = '/api/v1/users/me/video-playlists/videos-exist'
284
285 return makeGetRequest({
286 url,
287 token,
288 path,
289 query: { videoIds },
290 statusCodeExpected: HttpStatusCode.OK_200
291 })
292}
293
294// ---------------------------------------------------------------------------
295
296export {
297 getVideoPlaylistPrivacies,
298
299 getVideoPlaylistsList,
300 getVideoChannelPlaylistsList,
301 getAccountPlaylistsList,
302 getAccountPlaylistsListWithToken,
303
304 getVideoPlaylist,
305 getVideoPlaylistWithToken,
306
307 createVideoPlaylist,
308 updateVideoPlaylist,
309 deleteVideoPlaylist,
310
311 addVideoInPlaylist,
312 updateVideoPlaylistElement,
313 removeVideoFromPlaylist,
314
315 reorderVideosPlaylist,
316
317 checkPlaylistFilesWereRemoved,
318
319 doVideosExistInMyPlaylist
320}
diff --git a/shared/extra-utils/videos/video-streaming-playlists.ts b/shared/extra-utils/videos/video-streaming-playlists.ts
deleted file mode 100644
index 99c2e1880..000000000
--- a/shared/extra-utils/videos/video-streaming-playlists.ts
+++ /dev/null
@@ -1,82 +0,0 @@
1import { makeRawRequest } from '../requests/requests'
2import { sha256 } from '../../../server/helpers/core-utils'
3import { VideoStreamingPlaylist } from '../../models/videos/video-streaming-playlist.model'
4import { expect } from 'chai'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6
7function getPlaylist (url: string, statusCodeExpected = HttpStatusCode.OK_200) {
8 return makeRawRequest(url, statusCodeExpected)
9}
10
11function getSegment (url: string, statusCodeExpected = HttpStatusCode.OK_200, range?: string) {
12 return makeRawRequest(url, statusCodeExpected, range)
13}
14
15function getSegmentSha256 (url: string, statusCodeExpected = HttpStatusCode.OK_200) {
16 return makeRawRequest(url, statusCodeExpected)
17}
18
19async function checkSegmentHash (
20 baseUrlPlaylist: string,
21 baseUrlSegment: string,
22 videoUUID: string,
23 resolution: number,
24 hlsPlaylist: VideoStreamingPlaylist
25) {
26 const res = await getPlaylist(`${baseUrlPlaylist}/${videoUUID}/${resolution}.m3u8`)
27 const playlist = res.text
28
29 const videoName = `${videoUUID}-${resolution}-fragmented.mp4`
30
31 const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
32
33 const length = parseInt(matches[1], 10)
34 const offset = parseInt(matches[2], 10)
35 const range = `${offset}-${offset + length - 1}`
36
37 const res2 = await getSegment(`${baseUrlSegment}/${videoUUID}/${videoName}`, HttpStatusCode.PARTIAL_CONTENT_206, `bytes=${range}`)
38
39 const resSha = await getSegmentSha256(hlsPlaylist.segmentsSha256Url)
40
41 const sha256Server = resSha.body[videoName][range]
42 expect(sha256(res2.body)).to.equal(sha256Server)
43}
44
45async function checkLiveSegmentHash (
46 baseUrlSegment: string,
47 videoUUID: string,
48 segmentName: string,
49 hlsPlaylist: VideoStreamingPlaylist
50) {
51 const res2 = await getSegment(`${baseUrlSegment}/${videoUUID}/${segmentName}`)
52
53 const resSha = await getSegmentSha256(hlsPlaylist.segmentsSha256Url)
54
55 const sha256Server = resSha.body[segmentName]
56 expect(sha256(res2.body)).to.equal(sha256Server)
57}
58
59async function checkResolutionsInMasterPlaylist (playlistUrl: string, resolutions: number[]) {
60 const res = await getPlaylist(playlistUrl)
61
62 const masterPlaylist = res.text
63
64 for (const resolution of resolutions) {
65 const reg = new RegExp(
66 '#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"'
67 )
68
69 expect(masterPlaylist).to.match(reg)
70 }
71}
72
73// ---------------------------------------------------------------------------
74
75export {
76 getPlaylist,
77 getSegment,
78 checkResolutionsInMasterPlaylist,
79 getSegmentSha256,
80 checkLiveSegmentHash,
81 checkSegmentHash
82}
diff --git a/shared/extra-utils/videos/videos-command.ts b/shared/extra-utils/videos/videos-command.ts
new file mode 100644
index 000000000..98465e8f6
--- /dev/null
+++ b/shared/extra-utils/videos/videos-command.ts
@@ -0,0 +1,598 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2
3import { expect } from 'chai'
4import { createReadStream, stat } from 'fs-extra'
5import got, { Response as GotResponse } from 'got'
6import { omit, pick } from 'lodash'
7import validator from 'validator'
8import { buildUUID } from '@server/helpers/uuid'
9import { loadLanguages } from '@server/initializers/constants'
10import {
11 HttpStatusCode,
12 ResultList,
13 UserVideoRateType,
14 Video,
15 VideoCreate,
16 VideoCreateResult,
17 VideoDetails,
18 VideoFileMetadata,
19 VideoPrivacy,
20 VideosCommonQuery,
21 VideosWithSearchCommonQuery
22} from '@shared/models'
23import { buildAbsoluteFixturePath, wait } from '../miscs'
24import { unwrapBody } from '../requests'
25import { PeerTubeServer, waitJobs } from '../server'
26import { AbstractCommand, OverrideCommandOptions } from '../shared'
27
28export type VideoEdit = Partial<Omit<VideoCreate, 'thumbnailfile' | 'previewfile'>> & {
29 fixture?: string
30 thumbnailfile?: string
31 previewfile?: string
32}
33
34export class VideosCommand extends AbstractCommand {
35
36 constructor (server: PeerTubeServer) {
37 super(server)
38
39 loadLanguages()
40 }
41
42 getCategories (options: OverrideCommandOptions = {}) {
43 const path = '/api/v1/videos/categories'
44
45 return this.getRequestBody<{ [id: number]: string }>({
46 ...options,
47 path,
48
49 implicitToken: false,
50 defaultExpectedStatus: HttpStatusCode.OK_200
51 })
52 }
53
54 getLicences (options: OverrideCommandOptions = {}) {
55 const path = '/api/v1/videos/licences'
56
57 return this.getRequestBody<{ [id: number]: string }>({
58 ...options,
59 path,
60
61 implicitToken: false,
62 defaultExpectedStatus: HttpStatusCode.OK_200
63 })
64 }
65
66 getLanguages (options: OverrideCommandOptions = {}) {
67 const path = '/api/v1/videos/languages'
68
69 return this.getRequestBody<{ [id: string]: string }>({
70 ...options,
71 path,
72
73 implicitToken: false,
74 defaultExpectedStatus: HttpStatusCode.OK_200
75 })
76 }
77
78 getPrivacies (options: OverrideCommandOptions = {}) {
79 const path = '/api/v1/videos/privacies'
80
81 return this.getRequestBody<{ [id in VideoPrivacy]: string }>({
82 ...options,
83 path,
84
85 implicitToken: false,
86 defaultExpectedStatus: HttpStatusCode.OK_200
87 })
88 }
89
90 // ---------------------------------------------------------------------------
91
92 getDescription (options: OverrideCommandOptions & {
93 descriptionPath: string
94 }) {
95 return this.getRequestBody<{ description: string }>({
96 ...options,
97 path: options.descriptionPath,
98
99 implicitToken: false,
100 defaultExpectedStatus: HttpStatusCode.OK_200
101 })
102 }
103
104 getFileMetadata (options: OverrideCommandOptions & {
105 url: string
106 }) {
107 return unwrapBody<VideoFileMetadata>(this.getRawRequest({
108 ...options,
109
110 url: options.url,
111 implicitToken: false,
112 defaultExpectedStatus: HttpStatusCode.OK_200
113 }))
114 }
115
116 // ---------------------------------------------------------------------------
117
118 view (options: OverrideCommandOptions & {
119 id: number | string
120 xForwardedFor?: string
121 }) {
122 const { id, xForwardedFor } = options
123 const path = '/api/v1/videos/' + id + '/views'
124
125 return this.postBodyRequest({
126 ...options,
127
128 path,
129 xForwardedFor,
130 implicitToken: false,
131 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
132 })
133 }
134
135 rate (options: OverrideCommandOptions & {
136 id: number | string
137 rating: UserVideoRateType
138 }) {
139 const { id, rating } = options
140 const path = '/api/v1/videos/' + id + '/rate'
141
142 return this.putBodyRequest({
143 ...options,
144
145 path,
146 fields: { rating },
147 implicitToken: true,
148 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
149 })
150 }
151
152 // ---------------------------------------------------------------------------
153
154 get (options: OverrideCommandOptions & {
155 id: number | string
156 }) {
157 const path = '/api/v1/videos/' + options.id
158
159 return this.getRequestBody<VideoDetails>({
160 ...options,
161
162 path,
163 implicitToken: false,
164 defaultExpectedStatus: HttpStatusCode.OK_200
165 })
166 }
167
168 getWithToken (options: OverrideCommandOptions & {
169 id: number | string
170 }) {
171 return this.get({
172 ...options,
173
174 token: this.buildCommonRequestToken({ ...options, implicitToken: true })
175 })
176 }
177
178 async getId (options: OverrideCommandOptions & {
179 uuid: number | string
180 }) {
181 const { uuid } = options
182
183 if (validator.isUUID('' + uuid) === false) return uuid as number
184
185 const { id } = await this.get({ ...options, id: uuid })
186
187 return id
188 }
189
190 // ---------------------------------------------------------------------------
191
192 listMyVideos (options: OverrideCommandOptions & {
193 start?: number
194 count?: number
195 sort?: string
196 search?: string
197 isLive?: boolean
198 } = {}) {
199 const path = '/api/v1/users/me/videos'
200
201 return this.getRequestBody<ResultList<Video>>({
202 ...options,
203
204 path,
205 query: pick(options, [ 'start', 'count', 'sort', 'search', 'isLive' ]),
206 implicitToken: true,
207 defaultExpectedStatus: HttpStatusCode.OK_200
208 })
209 }
210
211 // ---------------------------------------------------------------------------
212
213 list (options: OverrideCommandOptions & VideosCommonQuery = {}) {
214 const path = '/api/v1/videos'
215
216 const query = this.buildListQuery(options)
217
218 return this.getRequestBody<ResultList<Video>>({
219 ...options,
220
221 path,
222 query: { sort: 'name', ...query },
223 implicitToken: false,
224 defaultExpectedStatus: HttpStatusCode.OK_200
225 })
226 }
227
228 listWithToken (options: OverrideCommandOptions & VideosCommonQuery = {}) {
229 return this.list({
230 ...options,
231
232 token: this.buildCommonRequestToken({ ...options, implicitToken: true })
233 })
234 }
235
236 listByAccount (options: OverrideCommandOptions & VideosWithSearchCommonQuery & {
237 handle: string
238 }) {
239 const { handle, search } = options
240 const path = '/api/v1/accounts/' + handle + '/videos'
241
242 return this.getRequestBody<ResultList<Video>>({
243 ...options,
244
245 path,
246 query: { search, ...this.buildListQuery(options) },
247 implicitToken: true,
248 defaultExpectedStatus: HttpStatusCode.OK_200
249 })
250 }
251
252 listByChannel (options: OverrideCommandOptions & VideosWithSearchCommonQuery & {
253 handle: string
254 }) {
255 const { handle } = options
256 const path = '/api/v1/video-channels/' + handle + '/videos'
257
258 return this.getRequestBody<ResultList<Video>>({
259 ...options,
260
261 path,
262 query: this.buildListQuery(options),
263 implicitToken: true,
264 defaultExpectedStatus: HttpStatusCode.OK_200
265 })
266 }
267
268 // ---------------------------------------------------------------------------
269
270 async find (options: OverrideCommandOptions & {
271 name: string
272 }) {
273 const { data } = await this.list(options)
274
275 return data.find(v => v.name === options.name)
276 }
277
278 // ---------------------------------------------------------------------------
279
280 update (options: OverrideCommandOptions & {
281 id: number | string
282 attributes?: VideoEdit
283 }) {
284 const { id, attributes = {} } = options
285 const path = '/api/v1/videos/' + id
286
287 // Upload request
288 if (attributes.thumbnailfile || attributes.previewfile) {
289 const attaches: any = {}
290 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
291 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
292
293 return this.putUploadRequest({
294 ...options,
295
296 path,
297 fields: options.attributes,
298 attaches: {
299 thumbnailfile: attributes.thumbnailfile,
300 previewfile: attributes.previewfile
301 },
302 implicitToken: true,
303 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
304 })
305 }
306
307 return this.putBodyRequest({
308 ...options,
309
310 path,
311 fields: options.attributes,
312 implicitToken: true,
313 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
314 })
315 }
316
317 remove (options: OverrideCommandOptions & {
318 id: number | string
319 }) {
320 const path = '/api/v1/videos/' + options.id
321
322 return unwrapBody(this.deleteRequest({
323 ...options,
324
325 path,
326 implicitToken: true,
327 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
328 }))
329 }
330
331 async removeAll () {
332 const { data } = await this.list()
333
334 for (const v of data) {
335 await this.remove({ id: v.id })
336 }
337 }
338
339 // ---------------------------------------------------------------------------
340
341 async upload (options: OverrideCommandOptions & {
342 attributes?: VideoEdit
343 mode?: 'legacy' | 'resumable' // default legacy
344 } = {}) {
345 const { mode = 'legacy' } = options
346 let defaultChannelId = 1
347
348 try {
349 const { videoChannels } = await this.server.users.getMyInfo({ token: options.token })
350 defaultChannelId = videoChannels[0].id
351 } catch (e) { /* empty */ }
352
353 // Override default attributes
354 const attributes = {
355 name: 'my super video',
356 category: 5,
357 licence: 4,
358 language: 'zh',
359 channelId: defaultChannelId,
360 nsfw: true,
361 waitTranscoding: false,
362 description: 'my super description',
363 support: 'my super support text',
364 tags: [ 'tag' ],
365 privacy: VideoPrivacy.PUBLIC,
366 commentsEnabled: true,
367 downloadEnabled: true,
368 fixture: 'video_short.webm',
369
370 ...options.attributes
371 }
372
373 const created = mode === 'legacy'
374 ? await this.buildLegacyUpload({ ...options, attributes })
375 : await this.buildResumeUpload({ ...options, attributes })
376
377 // Wait torrent generation
378 const expectedStatus = this.buildExpectedStatus({ ...options, defaultExpectedStatus: HttpStatusCode.OK_200 })
379 if (expectedStatus === HttpStatusCode.OK_200) {
380 let video: VideoDetails
381
382 do {
383 video = await this.getWithToken({ ...options, id: created.uuid })
384
385 await wait(50)
386 } while (!video.files[0].torrentUrl)
387 }
388
389 return created
390 }
391
392 async buildLegacyUpload (options: OverrideCommandOptions & {
393 attributes: VideoEdit
394 }): Promise<VideoCreateResult> {
395 const path = '/api/v1/videos/upload'
396
397 return unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({
398 ...options,
399
400 path,
401 fields: this.buildUploadFields(options.attributes),
402 attaches: this.buildUploadAttaches(options.attributes),
403 implicitToken: true,
404 defaultExpectedStatus: HttpStatusCode.OK_200
405 })).then(body => body.video || body as any)
406 }
407
408 async buildResumeUpload (options: OverrideCommandOptions & {
409 attributes: VideoEdit
410 }): Promise<VideoCreateResult> {
411 const { attributes, expectedStatus } = options
412
413 let size = 0
414 let videoFilePath: string
415 let mimetype = 'video/mp4'
416
417 if (attributes.fixture) {
418 videoFilePath = buildAbsoluteFixturePath(attributes.fixture)
419 size = (await stat(videoFilePath)).size
420
421 if (videoFilePath.endsWith('.mkv')) {
422 mimetype = 'video/x-matroska'
423 } else if (videoFilePath.endsWith('.webm')) {
424 mimetype = 'video/webm'
425 }
426 }
427
428 // Do not check status automatically, we'll check it manually
429 const initializeSessionRes = await this.prepareResumableUpload({ ...options, expectedStatus: null, attributes, size, mimetype })
430 const initStatus = initializeSessionRes.status
431
432 if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) {
433 const locationHeader = initializeSessionRes.header['location']
434 expect(locationHeader).to.not.be.undefined
435
436 const pathUploadId = locationHeader.split('?')[1]
437
438 const result = await this.sendResumableChunks({ ...options, pathUploadId, videoFilePath, size })
439
440 return result.body?.video || result.body as any
441 }
442
443 const expectedInitStatus = expectedStatus === HttpStatusCode.OK_200
444 ? HttpStatusCode.CREATED_201
445 : expectedStatus
446
447 expect(initStatus).to.equal(expectedInitStatus)
448
449 return initializeSessionRes.body.video || initializeSessionRes.body
450 }
451
452 async prepareResumableUpload (options: OverrideCommandOptions & {
453 attributes: VideoEdit
454 size: number
455 mimetype: string
456 }) {
457 const { attributes, size, mimetype } = options
458
459 const path = '/api/v1/videos/upload-resumable'
460
461 return this.postUploadRequest({
462 ...options,
463
464 path,
465 headers: {
466 'X-Upload-Content-Type': mimetype,
467 'X-Upload-Content-Length': size.toString()
468 },
469 fields: { filename: attributes.fixture, ...this.buildUploadFields(options.attributes) },
470 // Fixture will be sent later
471 attaches: this.buildUploadAttaches(omit(options.attributes, 'fixture')),
472 implicitToken: true,
473
474 defaultExpectedStatus: null
475 })
476 }
477
478 sendResumableChunks (options: OverrideCommandOptions & {
479 pathUploadId: string
480 videoFilePath: string
481 size: number
482 contentLength?: number
483 contentRangeBuilder?: (start: number, chunk: any) => string
484 }) {
485 const { pathUploadId, videoFilePath, size, contentLength, contentRangeBuilder, expectedStatus = HttpStatusCode.OK_200 } = options
486
487 const path = '/api/v1/videos/upload-resumable'
488 let start = 0
489
490 const token = this.buildCommonRequestToken({ ...options, implicitToken: true })
491 const url = this.server.url
492
493 const readable = createReadStream(videoFilePath, { highWaterMark: 8 * 1024 })
494 return new Promise<GotResponse<{ video: VideoCreateResult }>>((resolve, reject) => {
495 readable.on('data', async function onData (chunk) {
496 readable.pause()
497
498 const headers = {
499 'Authorization': 'Bearer ' + token,
500 'Content-Type': 'application/octet-stream',
501 'Content-Range': contentRangeBuilder
502 ? contentRangeBuilder(start, chunk)
503 : `bytes ${start}-${start + chunk.length - 1}/${size}`,
504 'Content-Length': contentLength ? contentLength + '' : chunk.length + ''
505 }
506
507 const res = await got<{ video: VideoCreateResult }>({
508 url,
509 method: 'put',
510 headers,
511 path: path + '?' + pathUploadId,
512 body: chunk,
513 responseType: 'json',
514 throwHttpErrors: false
515 })
516
517 start += chunk.length
518
519 if (res.statusCode === expectedStatus) {
520 return resolve(res)
521 }
522
523 if (res.statusCode !== HttpStatusCode.PERMANENT_REDIRECT_308) {
524 readable.off('data', onData)
525 return reject(new Error('Incorrect transient behaviour sending intermediary chunks'))
526 }
527
528 readable.resume()
529 })
530 })
531 }
532
533 quickUpload (options: OverrideCommandOptions & {
534 name: string
535 nsfw?: boolean
536 privacy?: VideoPrivacy
537 fixture?: string
538 }) {
539 const attributes: VideoEdit = { name: options.name }
540 if (options.nsfw) attributes.nsfw = options.nsfw
541 if (options.privacy) attributes.privacy = options.privacy
542 if (options.fixture) attributes.fixture = options.fixture
543
544 return this.upload({ ...options, attributes })
545 }
546
547 async randomUpload (options: OverrideCommandOptions & {
548 wait?: boolean // default true
549 additionalParams?: VideoEdit & { prefixName?: string }
550 } = {}) {
551 const { wait = true, additionalParams } = options
552 const prefixName = additionalParams?.prefixName || ''
553 const name = prefixName + buildUUID()
554
555 const attributes = { name, ...additionalParams }
556
557 const result = await this.upload({ ...options, attributes })
558
559 if (wait) await waitJobs([ this.server ])
560
561 return { ...result, name }
562 }
563
564 // ---------------------------------------------------------------------------
565
566 private buildListQuery (options: VideosCommonQuery) {
567 return pick(options, [
568 'start',
569 'count',
570 'sort',
571 'nsfw',
572 'isLive',
573 'categoryOneOf',
574 'licenceOneOf',
575 'languageOneOf',
576 'tagsOneOf',
577 'tagsAllOf',
578 'filter',
579 'skipCount'
580 ])
581 }
582
583 private buildUploadFields (attributes: VideoEdit) {
584 return omit(attributes, [ 'fixture', 'thumbnailfile', 'previewfile' ])
585 }
586
587 private buildUploadAttaches (attributes: VideoEdit) {
588 const attaches: { [ name: string ]: string } = {}
589
590 for (const key of [ 'thumbnailfile', 'previewfile' ]) {
591 if (attributes[key]) attaches[key] = buildAbsoluteFixturePath(attributes[key])
592 }
593
594 if (attributes.fixture) attaches.videofile = buildAbsoluteFixturePath(attributes.fixture)
595
596 return attaches
597 }
598}
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index 469ea4d63..9a9bfb3cf 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -1,348 +1,20 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2 2
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { createReadStream, pathExists, readdir, readFile, stat } from 'fs-extra' 4import { pathExists, readdir } from 'fs-extra'
5import got, { Response as GotResponse } from 'got/dist/source'
6import * as parseTorrent from 'parse-torrent'
7import { join } from 'path' 5import { join } from 'path'
8import * as request from 'supertest'
9import validator from 'validator'
10import { getLowercaseExtension } from '@server/helpers/core-utils' 6import { getLowercaseExtension } from '@server/helpers/core-utils'
11import { buildUUID } from '@server/helpers/uuid' 7import { HttpStatusCode } from '@shared/models'
12import { HttpStatusCode } from '@shared/core-utils' 8import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
13import { VideosCommonQuery } from '@shared/models' 9import { dateIsValid, testImage, webtorrentAdd } from '../miscs'
14import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants' 10import { makeRawRequest } from '../requests/requests'
15import { VideoDetails, VideoPrivacy } from '../../models/videos' 11import { waitJobs } from '../server'
16import { 12import { PeerTubeServer } from '../server/server'
17 buildAbsoluteFixturePath, 13import { VideoEdit } from './videos-command'
18 buildServerDirectory,
19 dateIsValid,
20 immutableAssign,
21 testImage,
22 wait,
23 webtorrentAdd
24} from '../miscs/miscs'
25import { makeGetRequest, makePutBodyRequest, makeRawRequest, makeUploadRequest } from '../requests/requests'
26import { waitJobs } from '../server/jobs'
27import { ServerInfo } from '../server/servers'
28import { getMyUserInformation } from '../users/users'
29
30loadLanguages()
31
32type VideoAttributes = {
33 name?: string
34 category?: number
35 licence?: number
36 language?: string
37 nsfw?: boolean
38 commentsEnabled?: boolean
39 downloadEnabled?: boolean
40 waitTranscoding?: boolean
41 description?: string
42 originallyPublishedAt?: string
43 tags?: string[]
44 channelId?: number
45 privacy?: VideoPrivacy
46 fixture?: string
47 support?: string
48 thumbnailfile?: string
49 previewfile?: string
50 scheduleUpdate?: {
51 updateAt: string
52 privacy?: VideoPrivacy
53 }
54}
55
56function getVideoCategories (url: string) {
57 const path = '/api/v1/videos/categories'
58
59 return makeGetRequest({
60 url,
61 path,
62 statusCodeExpected: HttpStatusCode.OK_200
63 })
64}
65
66function getVideoLicences (url: string) {
67 const path = '/api/v1/videos/licences'
68
69 return makeGetRequest({
70 url,
71 path,
72 statusCodeExpected: HttpStatusCode.OK_200
73 })
74}
75
76function getVideoLanguages (url: string) {
77 const path = '/api/v1/videos/languages'
78
79 return makeGetRequest({
80 url,
81 path,
82 statusCodeExpected: HttpStatusCode.OK_200
83 })
84}
85
86function getVideoPrivacies (url: string) {
87 const path = '/api/v1/videos/privacies'
88
89 return makeGetRequest({
90 url,
91 path,
92 statusCodeExpected: HttpStatusCode.OK_200
93 })
94}
95
96function getVideo (url: string, id: number | string, expectedStatus = HttpStatusCode.OK_200) {
97 const path = '/api/v1/videos/' + id
98
99 return request(url)
100 .get(path)
101 .set('Accept', 'application/json')
102 .expect(expectedStatus)
103}
104
105async function getVideoIdFromUUID (url: string, uuid: string) {
106 const res = await getVideo(url, uuid)
107
108 return res.body.id
109}
110
111function getVideoFileMetadataUrl (url: string) {
112 return request(url)
113 .get('/')
114 .set('Accept', 'application/json')
115 .expect(HttpStatusCode.OK_200)
116 .expect('Content-Type', /json/)
117}
118
119function viewVideo (url: string, id: number | string, expectedStatus = HttpStatusCode.NO_CONTENT_204, xForwardedFor?: string) {
120 const path = '/api/v1/videos/' + id + '/views'
121
122 const req = request(url)
123 .post(path)
124 .set('Accept', 'application/json')
125
126 if (xForwardedFor) {
127 req.set('X-Forwarded-For', xForwardedFor)
128 }
129
130 return req.expect(expectedStatus)
131}
132
133function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = HttpStatusCode.OK_200) {
134 const path = '/api/v1/videos/' + id
135
136 return request(url)
137 .get(path)
138 .set('Authorization', 'Bearer ' + token)
139 .set('Accept', 'application/json')
140 .expect(expectedStatus)
141}
142
143function getVideoDescription (url: string, descriptionPath: string) {
144 return request(url)
145 .get(descriptionPath)
146 .set('Accept', 'application/json')
147 .expect(HttpStatusCode.OK_200)
148 .expect('Content-Type', /json/)
149}
150
151function getVideosList (url: string) {
152 const path = '/api/v1/videos'
153
154 return request(url)
155 .get(path)
156 .query({ sort: 'name' })
157 .set('Accept', 'application/json')
158 .expect(HttpStatusCode.OK_200)
159 .expect('Content-Type', /json/)
160}
161
162function getVideosListWithToken (url: string, token: string, query: { nsfw?: boolean } = {}) {
163 const path = '/api/v1/videos'
164
165 return request(url)
166 .get(path)
167 .set('Authorization', 'Bearer ' + token)
168 .query(immutableAssign(query, { sort: 'name' }))
169 .set('Accept', 'application/json')
170 .expect(HttpStatusCode.OK_200)
171 .expect('Content-Type', /json/)
172}
173
174function getLocalVideos (url: string) {
175 const path = '/api/v1/videos'
176
177 return request(url)
178 .get(path)
179 .query({ sort: 'name', filter: 'local' })
180 .set('Accept', 'application/json')
181 .expect(HttpStatusCode.OK_200)
182 .expect('Content-Type', /json/)
183}
184
185function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string, search?: string) {
186 const path = '/api/v1/users/me/videos'
187
188 const req = request(url)
189 .get(path)
190 .query({ start: start })
191 .query({ count: count })
192 .query({ search: search })
193
194 if (sort) req.query({ sort })
195
196 return req.set('Accept', 'application/json')
197 .set('Authorization', 'Bearer ' + accessToken)
198 .expect(HttpStatusCode.OK_200)
199 .expect('Content-Type', /json/)
200}
201
202function getMyVideosWithFilter (url: string, accessToken: string, query: { isLive?: boolean }) {
203 const path = '/api/v1/users/me/videos'
204
205 return makeGetRequest({
206 url,
207 path,
208 token: accessToken,
209 query,
210 statusCodeExpected: HttpStatusCode.OK_200
211 })
212}
213
214function getAccountVideos (
215 url: string,
216 accessToken: string,
217 accountName: string,
218 start: number,
219 count: number,
220 sort?: string,
221 query: {
222 nsfw?: boolean
223 search?: string
224 } = {}
225) {
226 const path = '/api/v1/accounts/' + accountName + '/videos'
227
228 return makeGetRequest({
229 url,
230 path,
231 query: immutableAssign(query, {
232 start,
233 count,
234 sort
235 }),
236 token: accessToken,
237 statusCodeExpected: HttpStatusCode.OK_200
238 })
239}
240
241function getVideoChannelVideos (
242 url: string,
243 accessToken: string,
244 videoChannelName: string,
245 start: number,
246 count: number,
247 sort?: string,
248 query: { nsfw?: boolean } = {}
249) {
250 const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
251
252 return makeGetRequest({
253 url,
254 path,
255 query: immutableAssign(query, {
256 start,
257 count,
258 sort
259 }),
260 token: accessToken,
261 statusCodeExpected: HttpStatusCode.OK_200
262 })
263}
264
265function getPlaylistVideos (
266 url: string,
267 accessToken: string,
268 playlistId: number | string,
269 start: number,
270 count: number,
271 query: { nsfw?: boolean } = {}
272) {
273 const path = '/api/v1/video-playlists/' + playlistId + '/videos'
274
275 return makeGetRequest({
276 url,
277 path,
278 query: immutableAssign(query, {
279 start,
280 count
281 }),
282 token: accessToken,
283 statusCodeExpected: HttpStatusCode.OK_200
284 })
285}
286
287function getVideosListPagination (url: string, start: number, count: number, sort?: string, skipCount?: boolean) {
288 const path = '/api/v1/videos'
289
290 const req = request(url)
291 .get(path)
292 .query({ start: start })
293 .query({ count: count })
294
295 if (sort) req.query({ sort })
296 if (skipCount) req.query({ skipCount })
297
298 return req.set('Accept', 'application/json')
299 .expect(HttpStatusCode.OK_200)
300 .expect('Content-Type', /json/)
301}
302
303function getVideosListSort (url: string, sort: string) {
304 const path = '/api/v1/videos'
305
306 return request(url)
307 .get(path)
308 .query({ sort: sort })
309 .set('Accept', 'application/json')
310 .expect(HttpStatusCode.OK_200)
311 .expect('Content-Type', /json/)
312}
313
314function getVideosWithFilters (url: string, query: VideosCommonQuery) {
315 const path = '/api/v1/videos'
316
317 return request(url)
318 .get(path)
319 .query(query)
320 .set('Accept', 'application/json')
321 .expect(HttpStatusCode.OK_200)
322 .expect('Content-Type', /json/)
323}
324
325function removeVideo (url: string, token: string, id: number | string, expectedStatus = HttpStatusCode.NO_CONTENT_204) {
326 const path = '/api/v1/videos'
327
328 return request(url)
329 .delete(path + '/' + id)
330 .set('Accept', 'application/json')
331 .set('Authorization', 'Bearer ' + token)
332 .expect(expectedStatus)
333}
334
335async function removeAllVideos (server: ServerInfo) {
336 const resVideos = await getVideosList(server.url)
337
338 for (const v of resVideos.body.data) {
339 await removeVideo(server.url, server.accessToken, v.id)
340 }
341}
342 14
343async function checkVideoFilesWereRemoved ( 15async function checkVideoFilesWereRemoved (
344 videoUUID: string, 16 videoUUID: string,
345 serverNumber: number, 17 server: PeerTubeServer,
346 directories = [ 18 directories = [
347 'redundancy', 19 'redundancy',
348 'videos', 20 'videos',
@@ -355,7 +27,7 @@ async function checkVideoFilesWereRemoved (
355 ] 27 ]
356) { 28) {
357 for (const directory of directories) { 29 for (const directory of directories) {
358 const directoryPath = buildServerDirectory({ internalServerNumber: serverNumber }, directory) 30 const directoryPath = server.servers.buildDirectory(directory)
359 31
360 const directoryExists = await pathExists(directoryPath) 32 const directoryExists = await pathExists(directoryPath)
361 if (directoryExists === false) continue 33 if (directoryExists === false) continue
@@ -367,280 +39,20 @@ async function checkVideoFilesWereRemoved (
367 } 39 }
368} 40}
369 41
370async function uploadVideo (
371 url: string,
372 accessToken: string,
373 videoAttributesArg: VideoAttributes,
374 specialStatus = HttpStatusCode.OK_200,
375 mode: 'legacy' | 'resumable' = 'legacy'
376) {
377 let defaultChannelId = '1'
378
379 try {
380 const res = await getMyUserInformation(url, accessToken)
381 defaultChannelId = res.body.videoChannels[0].id
382 } catch (e) { /* empty */ }
383
384 // Override default attributes
385 const attributes = Object.assign({
386 name: 'my super video',
387 category: 5,
388 licence: 4,
389 language: 'zh',
390 channelId: defaultChannelId,
391 nsfw: true,
392 waitTranscoding: false,
393 description: 'my super description',
394 support: 'my super support text',
395 tags: [ 'tag' ],
396 privacy: VideoPrivacy.PUBLIC,
397 commentsEnabled: true,
398 downloadEnabled: true,
399 fixture: 'video_short.webm'
400 }, videoAttributesArg)
401
402 const res = mode === 'legacy'
403 ? await buildLegacyUpload(url, accessToken, attributes, specialStatus)
404 : await buildResumeUpload(url, accessToken, attributes, specialStatus)
405
406 // Wait torrent generation
407 if (specialStatus === HttpStatusCode.OK_200) {
408 let video: VideoDetails
409 do {
410 const resVideo = await getVideoWithToken(url, accessToken, res.body.video.uuid)
411 video = resVideo.body
412
413 await wait(50)
414 } while (!video.files[0].torrentUrl)
415 }
416
417 return res
418}
419
420function checkUploadVideoParam ( 42function checkUploadVideoParam (
421 url: string, 43 server: PeerTubeServer,
422 token: string, 44 token: string,
423 attributes: Partial<VideoAttributes>, 45 attributes: Partial<VideoEdit>,
424 specialStatus = HttpStatusCode.OK_200, 46 expectedStatus = HttpStatusCode.OK_200,
425 mode: 'legacy' | 'resumable' = 'legacy' 47 mode: 'legacy' | 'resumable' = 'legacy'
426) { 48) {
427 return mode === 'legacy' 49 return mode === 'legacy'
428 ? buildLegacyUpload(url, token, attributes, specialStatus) 50 ? server.videos.buildLegacyUpload({ token, attributes, expectedStatus })
429 : buildResumeUpload(url, token, attributes, specialStatus) 51 : server.videos.buildResumeUpload({ token, attributes, expectedStatus })
430}
431
432async function buildLegacyUpload (url: string, token: string, attributes: VideoAttributes, specialStatus = HttpStatusCode.OK_200) {
433 const path = '/api/v1/videos/upload'
434 const req = request(url)
435 .post(path)
436 .set('Accept', 'application/json')
437 .set('Authorization', 'Bearer ' + token)
438
439 buildUploadReq(req, attributes)
440
441 if (attributes.fixture !== undefined) {
442 req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
443 }
444
445 return req.expect(specialStatus)
446}
447
448async function buildResumeUpload (url: string, token: string, attributes: VideoAttributes, specialStatus = HttpStatusCode.OK_200) {
449 let size = 0
450 let videoFilePath: string
451 let mimetype = 'video/mp4'
452
453 if (attributes.fixture) {
454 videoFilePath = buildAbsoluteFixturePath(attributes.fixture)
455 size = (await stat(videoFilePath)).size
456
457 if (videoFilePath.endsWith('.mkv')) {
458 mimetype = 'video/x-matroska'
459 } else if (videoFilePath.endsWith('.webm')) {
460 mimetype = 'video/webm'
461 }
462 }
463
464 const initializeSessionRes = await prepareResumableUpload({ url, token, attributes, size, mimetype })
465 const initStatus = initializeSessionRes.status
466
467 if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) {
468 const locationHeader = initializeSessionRes.header['location']
469 expect(locationHeader).to.not.be.undefined
470
471 const pathUploadId = locationHeader.split('?')[1]
472
473 return sendResumableChunks({ url, token, pathUploadId, videoFilePath, size, specialStatus })
474 }
475
476 const expectedInitStatus = specialStatus === HttpStatusCode.OK_200
477 ? HttpStatusCode.CREATED_201
478 : specialStatus
479
480 expect(initStatus).to.equal(expectedInitStatus)
481
482 return initializeSessionRes
483}
484
485async function prepareResumableUpload (options: {
486 url: string
487 token: string
488 attributes: VideoAttributes
489 size: number
490 mimetype: string
491}) {
492 const { url, token, attributes, size, mimetype } = options
493
494 const path = '/api/v1/videos/upload-resumable'
495
496 const req = request(url)
497 .post(path)
498 .set('Authorization', 'Bearer ' + token)
499 .set('X-Upload-Content-Type', mimetype)
500 .set('X-Upload-Content-Length', size.toString())
501
502 buildUploadReq(req, attributes)
503
504 if (attributes.fixture) {
505 req.field('filename', attributes.fixture)
506 }
507
508 return req
509}
510
511function sendResumableChunks (options: {
512 url: string
513 token: string
514 pathUploadId: string
515 videoFilePath: string
516 size: number
517 specialStatus?: HttpStatusCode
518 contentLength?: number
519 contentRangeBuilder?: (start: number, chunk: any) => string
520}) {
521 const { url, token, pathUploadId, videoFilePath, size, specialStatus, contentLength, contentRangeBuilder } = options
522
523 const expectedStatus = specialStatus || HttpStatusCode.OK_200
524
525 const path = '/api/v1/videos/upload-resumable'
526 let start = 0
527
528 const readable = createReadStream(videoFilePath, { highWaterMark: 8 * 1024 })
529 return new Promise<GotResponse>((resolve, reject) => {
530 readable.on('data', async function onData (chunk) {
531 readable.pause()
532
533 const headers = {
534 'Authorization': 'Bearer ' + token,
535 'Content-Type': 'application/octet-stream',
536 'Content-Range': contentRangeBuilder
537 ? contentRangeBuilder(start, chunk)
538 : `bytes ${start}-${start + chunk.length - 1}/${size}`,
539 'Content-Length': contentLength ? contentLength + '' : chunk.length + ''
540 }
541
542 const res = await got({
543 url,
544 method: 'put',
545 headers,
546 path: path + '?' + pathUploadId,
547 body: chunk,
548 responseType: 'json',
549 throwHttpErrors: false
550 })
551
552 start += chunk.length
553
554 if (res.statusCode === expectedStatus) {
555 return resolve(res)
556 }
557
558 if (res.statusCode !== HttpStatusCode.PERMANENT_REDIRECT_308) {
559 readable.off('data', onData)
560 return reject(new Error('Incorrect transient behaviour sending intermediary chunks'))
561 }
562
563 readable.resume()
564 })
565 })
566}
567
568function updateVideo (
569 url: string,
570 accessToken: string,
571 id: number | string,
572 attributes: VideoAttributes,
573 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
574) {
575 const path = '/api/v1/videos/' + id
576 const body = {}
577
578 if (attributes.name) body['name'] = attributes.name
579 if (attributes.category) body['category'] = attributes.category
580 if (attributes.licence) body['licence'] = attributes.licence
581 if (attributes.language) body['language'] = attributes.language
582 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
583 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
584 if (attributes.downloadEnabled !== undefined) body['downloadEnabled'] = JSON.stringify(attributes.downloadEnabled)
585 if (attributes.originallyPublishedAt !== undefined) body['originallyPublishedAt'] = attributes.originallyPublishedAt
586 if (attributes.description) body['description'] = attributes.description
587 if (attributes.tags) body['tags'] = attributes.tags
588 if (attributes.privacy) body['privacy'] = attributes.privacy
589 if (attributes.channelId) body['channelId'] = attributes.channelId
590 if (attributes.scheduleUpdate) body['scheduleUpdate'] = attributes.scheduleUpdate
591
592 // Upload request
593 if (attributes.thumbnailfile || attributes.previewfile) {
594 const attaches: any = {}
595 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
596 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
597
598 return makeUploadRequest({
599 url,
600 method: 'PUT',
601 path,
602 token: accessToken,
603 fields: body,
604 attaches,
605 statusCodeExpected
606 })
607 }
608
609 return makePutBodyRequest({
610 url,
611 path,
612 fields: body,
613 token: accessToken,
614 statusCodeExpected
615 })
616}
617
618function rateVideo (url: string, accessToken: string, id: number | string, rating: string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
619 const path = '/api/v1/videos/' + id + '/rate'
620
621 return request(url)
622 .put(path)
623 .set('Accept', 'application/json')
624 .set('Authorization', 'Bearer ' + accessToken)
625 .send({ rating })
626 .expect(specialStatus)
627}
628
629function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
630 return new Promise<any>((res, rej) => {
631 const torrentName = videoUUID + '-' + resolution + '.torrent'
632 const torrentPath = buildServerDirectory(server, join('torrents', torrentName))
633
634 readFile(torrentPath, (err, data) => {
635 if (err) return rej(err)
636
637 return res(parseTorrent(data))
638 })
639 })
640} 52}
641 53
642async function completeVideoCheck ( 54async function completeVideoCheck (
643 url: string, 55 server: PeerTubeServer,
644 video: any, 56 video: any,
645 attributes: { 57 attributes: {
646 name: string 58 name: string
@@ -682,7 +94,7 @@ async function completeVideoCheck (
682 if (!attributes.likes) attributes.likes = 0 94 if (!attributes.likes) attributes.likes = 0
683 if (!attributes.dislikes) attributes.dislikes = 0 95 if (!attributes.dislikes) attributes.dislikes = 0
684 96
685 const host = new URL(url).host 97 const host = new URL(server.url).host
686 const originHost = attributes.account.host 98 const originHost = attributes.account.host
687 99
688 expect(video.name).to.equal(attributes.name) 100 expect(video.name).to.equal(attributes.name)
@@ -719,8 +131,7 @@ async function completeVideoCheck (
719 expect(video.originallyPublishedAt).to.be.null 131 expect(video.originallyPublishedAt).to.be.null
720 } 132 }
721 133
722 const res = await getVideo(url, video.uuid) 134 const videoDetails = await server.videos.get({ id: video.uuid })
723 const videoDetails: VideoDetails = res.body
724 135
725 expect(videoDetails.files).to.have.lengthOf(attributes.files.length) 136 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
726 expect(videoDetails.tags).to.deep.equal(attributes.tags) 137 expect(videoDetails.tags).to.deep.equal(attributes.tags)
@@ -776,149 +187,33 @@ async function completeVideoCheck (
776 } 187 }
777 188
778 expect(videoDetails.thumbnailPath).to.exist 189 expect(videoDetails.thumbnailPath).to.exist
779 await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath) 190 await testImage(server.url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
780 191
781 if (attributes.previewfile) { 192 if (attributes.previewfile) {
782 expect(videoDetails.previewPath).to.exist 193 expect(videoDetails.previewPath).to.exist
783 await testImage(url, attributes.previewfile, videoDetails.previewPath) 194 await testImage(server.url, attributes.previewfile, videoDetails.previewPath)
784 } 195 }
785} 196}
786 197
787async function videoUUIDToId (url: string, id: number | string) {
788 if (validator.isUUID('' + id) === false) return id
789
790 const res = await getVideo(url, id)
791 return res.body.id
792}
793
794async function uploadVideoAndGetId (options: {
795 server: ServerInfo
796 videoName: string
797 nsfw?: boolean
798 privacy?: VideoPrivacy
799 token?: string
800 fixture?: string
801}) {
802 const videoAttrs: any = { name: options.videoName }
803 if (options.nsfw) videoAttrs.nsfw = options.nsfw
804 if (options.privacy) videoAttrs.privacy = options.privacy
805 if (options.fixture) videoAttrs.fixture = options.fixture
806
807 const res = await uploadVideo(options.server.url, options.token || options.server.accessToken, videoAttrs)
808
809 return res.body.video as { id: number, uuid: string, shortUUID: string }
810}
811
812async function getLocalIdByUUID (url: string, uuid: string) {
813 const res = await getVideo(url, uuid)
814
815 return res.body.id
816}
817
818// serverNumber starts from 1 198// serverNumber starts from 1
819async function uploadRandomVideoOnServers (servers: ServerInfo[], serverNumber: number, additionalParams: any = {}) { 199async function uploadRandomVideoOnServers (
200 servers: PeerTubeServer[],
201 serverNumber: number,
202 additionalParams?: VideoEdit & { prefixName?: string }
203) {
820 const server = servers.find(s => s.serverNumber === serverNumber) 204 const server = servers.find(s => s.serverNumber === serverNumber)
821 const res = await uploadRandomVideo(server, false, additionalParams) 205 const res = await server.videos.randomUpload({ wait: false, additionalParams })
822 206
823 await waitJobs(servers) 207 await waitJobs(servers)
824 208
825 return res 209 return res
826} 210}
827 211
828async function uploadRandomVideo (server: ServerInfo, wait = true, additionalParams: any = {}) {
829 const prefixName = additionalParams.prefixName || ''
830 const name = prefixName + buildUUID()
831
832 const data = Object.assign({ name }, additionalParams)
833 const res = await uploadVideo(server.url, server.accessToken, data)
834
835 if (wait) await waitJobs([ server ])
836
837 return { uuid: res.body.video.uuid, name }
838}
839
840// --------------------------------------------------------------------------- 212// ---------------------------------------------------------------------------
841 213
842export { 214export {
843 getVideoDescription,
844 getVideoCategories,
845 uploadRandomVideo,
846 getVideoLicences,
847 videoUUIDToId,
848 getVideoPrivacies,
849 getVideoLanguages,
850 getMyVideos,
851 getAccountVideos,
852 getVideoChannelVideos,
853 getVideo,
854 getVideoFileMetadataUrl,
855 getVideoWithToken,
856 getVideosList,
857 removeAllVideos,
858 checkUploadVideoParam, 215 checkUploadVideoParam,
859 getVideosListPagination,
860 getVideosListSort,
861 removeVideo,
862 getVideosListWithToken,
863 uploadVideo,
864 sendResumableChunks,
865 getVideosWithFilters,
866 uploadRandomVideoOnServers,
867 updateVideo,
868 rateVideo,
869 viewVideo,
870 parseTorrentVideo,
871 getLocalVideos,
872 completeVideoCheck, 216 completeVideoCheck,
873 checkVideoFilesWereRemoved, 217 uploadRandomVideoOnServers,
874 getPlaylistVideos, 218 checkVideoFilesWereRemoved
875 getMyVideosWithFilter,
876 uploadVideoAndGetId,
877 getLocalIdByUUID,
878 getVideoIdFromUUID,
879 prepareResumableUpload
880}
881
882// ---------------------------------------------------------------------------
883
884function buildUploadReq (req: request.Test, attributes: VideoAttributes) {
885
886 for (const key of [ 'name', 'support', 'channelId', 'description', 'originallyPublishedAt' ]) {
887 if (attributes[key] !== undefined) {
888 req.field(key, attributes[key])
889 }
890 }
891
892 for (const key of [ 'nsfw', 'commentsEnabled', 'downloadEnabled', 'waitTranscoding' ]) {
893 if (attributes[key] !== undefined) {
894 req.field(key, JSON.stringify(attributes[key]))
895 }
896 }
897
898 for (const key of [ 'language', 'privacy', 'category', 'licence' ]) {
899 if (attributes[key] !== undefined) {
900 req.field(key, attributes[key].toString())
901 }
902 }
903
904 const tags = attributes.tags || []
905 for (let i = 0; i < tags.length; i++) {
906 req.field('tags[' + i + ']', attributes.tags[i])
907 }
908
909 for (const key of [ 'thumbnailfile', 'previewfile' ]) {
910 if (attributes[key] !== undefined) {
911 req.attach(key, buildAbsoluteFixturePath(attributes[key]))
912 }
913 }
914
915 if (attributes.scheduleUpdate) {
916 if (attributes.scheduleUpdate.updateAt) {
917 req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt)
918 }
919
920 if (attributes.scheduleUpdate.privacy) {
921 req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy)
922 }
923 }
924} 219}
diff --git a/shared/core-utils/miscs/http-error-codes.ts b/shared/models/http/http-error-codes.ts
index b2fbdfc5a..b2fbdfc5a 100644
--- a/shared/core-utils/miscs/http-error-codes.ts
+++ b/shared/models/http/http-error-codes.ts
diff --git a/shared/core-utils/miscs/http-methods.ts b/shared/models/http/http-methods.ts
index 1cfa458b9..1cfa458b9 100644
--- a/shared/core-utils/miscs/http-methods.ts
+++ b/shared/models/http/http-methods.ts
diff --git a/shared/models/http/index.ts b/shared/models/http/index.ts
new file mode 100644
index 000000000..ec991afe0
--- /dev/null
+++ b/shared/models/http/index.ts
@@ -0,0 +1,2 @@
1export * from './http-error-codes'
2export * from './http-methods'
diff --git a/shared/models/index.ts b/shared/models/index.ts
index 5c2bc480e..78723d830 100644
--- a/shared/models/index.ts
+++ b/shared/models/index.ts
@@ -4,6 +4,7 @@ export * from './bulk'
4export * from './common' 4export * from './common'
5export * from './custom-markup' 5export * from './custom-markup'
6export * from './feeds' 6export * from './feeds'
7export * from './http'
7export * from './joinpeertube' 8export * from './joinpeertube'
8export * from './moderation' 9export * from './moderation'
9export * from './overviews' 10export * from './overviews'
diff --git a/shared/models/search/videos-common-query.model.ts b/shared/models/search/videos-common-query.model.ts
index bd02489ea..179266338 100644
--- a/shared/models/search/videos-common-query.model.ts
+++ b/shared/models/search/videos-common-query.model.ts
@@ -21,6 +21,8 @@ export interface VideosCommonQuery {
21 tagsAllOf?: string[] 21 tagsAllOf?: string[]
22 22
23 filter?: VideoFilter 23 filter?: VideoFilter
24
25 skipCount?: boolean
24} 26}
25 27
26export interface VideosWithSearchCommonQuery extends VideosCommonQuery { 28export interface VideosWithSearchCommonQuery extends VideosCommonQuery {
diff --git a/shared/models/server/debug.model.ts b/shared/models/server/debug.model.ts
index 7ceff9137..2ecabdeca 100644
--- a/shared/models/server/debug.model.ts
+++ b/shared/models/server/debug.model.ts
@@ -1,5 +1,6 @@
1export interface Debug { 1export interface Debug {
2 ip: string 2 ip: string
3 activityPubMessagesWaiting: number
3} 4}
4 5
5export interface SendDebugCommand { 6export interface SendDebugCommand {
diff --git a/shared/models/server/index.ts b/shared/models/server/index.ts
index 06bf5c599..0f7646c7a 100644
--- a/shared/models/server/index.ts
+++ b/shared/models/server/index.ts
@@ -10,4 +10,5 @@ export * from './peertube-problem-document.model'
10export * from './server-config.model' 10export * from './server-config.model'
11export * from './server-debug.model' 11export * from './server-debug.model'
12export * from './server-error-code.enum' 12export * from './server-error-code.enum'
13export * from './server-follow-create.model'
13export * from './server-stats.model' 14export * from './server-stats.model'
diff --git a/shared/models/server/peertube-problem-document.model.ts b/shared/models/server/peertube-problem-document.model.ts
index e391d5aad..8dd96f7a3 100644
--- a/shared/models/server/peertube-problem-document.model.ts
+++ b/shared/models/server/peertube-problem-document.model.ts
@@ -1,4 +1,4 @@
1import { HttpStatusCode } from '../../core-utils' 1import { HttpStatusCode } from '../../models'
2import { OAuth2ErrorCode, ServerErrorCode } from './server-error-code.enum' 2import { OAuth2ErrorCode, ServerErrorCode } from './server-error-code.enum'
3 3
4export interface PeerTubeProblemDocumentData { 4export interface PeerTubeProblemDocumentData {
diff --git a/shared/models/server/server-follow-create.model.ts b/shared/models/server/server-follow-create.model.ts
new file mode 100644
index 000000000..3f90c7d6f
--- /dev/null
+++ b/shared/models/server/server-follow-create.model.ts
@@ -0,0 +1,4 @@
1export interface ServerFollowCreate {
2 hosts?: string[]
3 handles?: string[]
4}
diff --git a/shared/models/users/index.ts b/shared/models/users/index.ts
index a9d578054..b61a8cd40 100644
--- a/shared/models/users/index.ts
+++ b/shared/models/users/index.ts
@@ -1,3 +1,4 @@
1export * from './user-create-result.model'
1export * from './user-create.model' 2export * from './user-create.model'
2export * from './user-flag.model' 3export * from './user-flag.model'
3export * from './user-login.model' 4export * from './user-login.model'
diff --git a/shared/models/users/user-create-result.model.ts b/shared/models/users/user-create-result.model.ts
new file mode 100644
index 000000000..835b241ed
--- /dev/null
+++ b/shared/models/users/user-create-result.model.ts
@@ -0,0 +1,7 @@
1export interface UserCreateResult {
2 id: number
3
4 account: {
5 id: number
6 }
7}
diff --git a/shared/models/videos/channel/index.ts b/shared/models/videos/channel/index.ts
index 9dbaa42da..6cdabffbd 100644
--- a/shared/models/videos/channel/index.ts
+++ b/shared/models/videos/channel/index.ts
@@ -1,3 +1,4 @@
1export * from './video-channel-create-result.model'
1export * from './video-channel-create.model' 2export * from './video-channel-create.model'
2export * from './video-channel-update.model' 3export * from './video-channel-update.model'
3export * from './video-channel.model' 4export * from './video-channel.model'
diff --git a/shared/models/videos/channel/video-channel-create-result.model.ts b/shared/models/videos/channel/video-channel-create-result.model.ts
new file mode 100644
index 000000000..e3d7aeb4c
--- /dev/null
+++ b/shared/models/videos/channel/video-channel-create-result.model.ts
@@ -0,0 +1,3 @@
1export interface VideoChannelCreateResult {
2 id: number
3}
diff --git a/shared/models/videos/comment/index.ts b/shared/models/videos/comment/index.ts
index 7b9261a36..80c6c0724 100644
--- a/shared/models/videos/comment/index.ts
+++ b/shared/models/videos/comment/index.ts
@@ -1 +1,2 @@
1export * from './video-comment-create.model'
1export * from './video-comment.model' 2export * from './video-comment.model'
diff --git a/shared/models/videos/comment/video-comment-create.model.ts b/shared/models/videos/comment/video-comment-create.model.ts
new file mode 100644
index 000000000..1f0135405
--- /dev/null
+++ b/shared/models/videos/comment/video-comment-create.model.ts
@@ -0,0 +1,3 @@
1export interface VideoCommentCreate {
2 text: string
3}
diff --git a/shared/models/videos/comment/video-comment.model.ts b/shared/models/videos/comment/video-comment.model.ts
index 79c0e4c0a..5a96f9d4a 100644
--- a/shared/models/videos/comment/video-comment.model.ts
+++ b/shared/models/videos/comment/video-comment.model.ts
@@ -1,3 +1,4 @@
1import { ResultList } from '@shared/models/common'
1import { Account } from '../../actors' 2import { Account } from '../../actors'
2 3
3export interface VideoComment { 4export interface VideoComment {
@@ -36,11 +37,9 @@ export interface VideoCommentAdmin {
36 } 37 }
37} 38}
38 39
40export type VideoCommentThreads = ResultList<VideoComment> & { totalNotDeletedComments: number }
41
39export interface VideoCommentThreadTree { 42export interface VideoCommentThreadTree {
40 comment: VideoComment 43 comment: VideoComment
41 children: VideoCommentThreadTree[] 44 children: VideoCommentThreadTree[]
42} 45}
43
44export interface VideoCommentCreate {
45 text: string
46}
diff --git a/shared/models/videos/playlist/index.ts b/shared/models/videos/playlist/index.ts
index f11a4bd28..a9e8ce496 100644
--- a/shared/models/videos/playlist/index.ts
+++ b/shared/models/videos/playlist/index.ts
@@ -1,6 +1,7 @@
1export * from './video-exist-in-playlist.model' 1export * from './video-exist-in-playlist.model'
2export * from './video-playlist-create-result.model' 2export * from './video-playlist-create-result.model'
3export * from './video-playlist-create.model' 3export * from './video-playlist-create.model'
4export * from './video-playlist-element-create-result.model'
4export * from './video-playlist-element-create.model' 5export * from './video-playlist-element-create.model'
5export * from './video-playlist-element-update.model' 6export * from './video-playlist-element-update.model'
6export * from './video-playlist-element.model' 7export * from './video-playlist-element.model'
diff --git a/shared/models/videos/playlist/video-playlist-element-create-result.model.ts b/shared/models/videos/playlist/video-playlist-element-create-result.model.ts
new file mode 100644
index 000000000..dc475e7d8
--- /dev/null
+++ b/shared/models/videos/playlist/video-playlist-element-create-result.model.ts
@@ -0,0 +1,3 @@
1export interface VideoPlaylistElementCreateResult {
2 id: number
3}
diff --git a/shared/models/videos/video-update.model.ts b/shared/models/videos/video-update.model.ts
index e21ccae04..86653b959 100644
--- a/shared/models/videos/video-update.model.ts
+++ b/shared/models/videos/video-update.model.ts
@@ -1,5 +1,6 @@
1import { VideoPrivacy } from './video-privacy.enum' 1import { VideoPrivacy } from './video-privacy.enum'
2import { VideoScheduleUpdate } from './video-schedule-update.model' 2import { VideoScheduleUpdate } from './video-schedule-update.model'
3
3export interface VideoUpdate { 4export interface VideoUpdate {
4 name?: string 5 name?: string
5 category?: number 6 category?: number
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index 99a725ead..76e78fe53 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -716,7 +716,7 @@ paths:
716 - admin 716 - admin
717 tags: 717 tags:
718 - Instance Follows 718 - Instance Follows
719 summary: Follow a list of servers 719 summary: Follow a list of actors (PeerTube instance, channel or account)
720 responses: 720 responses:
721 '204': 721 '204':
722 description: successful operation 722 description: successful operation
@@ -734,28 +734,32 @@ paths:
734 type: string 734 type: string
735 format: hostname 735 format: hostname
736 uniqueItems: true 736 uniqueItems: true
737 handles:
738 type: array
739 items:
740 type: string
741 uniqueItems: true
737 742
738 '/server/following/{host}': 743 '/server/following/{hostOrHandle}':
739 delete: 744 delete:
740 summary: Unfollow a server 745 summary: Unfollow an actor (PeerTube instance, channel or account)
741 security: 746 security:
742 - OAuth2: 747 - OAuth2:
743 - admin 748 - admin
744 tags: 749 tags:
745 - Instance Follows 750 - Instance Follows
746 parameters: 751 parameters:
747 - name: host 752 - name: hostOrHandle
748 in: path 753 in: path
749 required: true 754 required: true
750 description: The host to unfollow 755 description: The hostOrHandle to unfollow
751 schema: 756 schema:
752 type: string 757 type: string
753 format: hostname
754 responses: 758 responses:
755 '204': 759 '204':
756 description: successful operation 760 description: successful operation
757 '404': 761 '404':
758 description: host not found 762 description: host or handle not found
759 763
760 /users: 764 /users:
761 post: 765 post:
diff --git a/tsconfig.json b/tsconfig.json
index d305722c4..32e4a42e4 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,6 +8,7 @@
8 "emitDecoratorMetadata": true, 8 "emitDecoratorMetadata": true,
9 "importHelpers": true, 9 "importHelpers": true,
10 "removeComments": true, 10 "removeComments": true,
11 "strictBindCallApply": true,
11 "outDir": "./dist", 12 "outDir": "./dist",
12 "lib": [ 13 "lib": [
13 "dom", 14 "dom",