]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Search video channel handle/uri
authorChocobozzz <me@florianbigard.com>
Fri, 24 Aug 2018 09:04:02 +0000 (11:04 +0200)
committerChocobozzz <me@florianbigard.com>
Mon, 27 Aug 2018 07:41:54 +0000 (09:41 +0200)
18 files changed:
client/src/app/+accounts/account-video-channels/account-video-channels.component.html
client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.html
client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
client/src/app/+video-channels/video-channels.component.scss
client/src/app/search/search.component.html
client/src/app/shared/video/video.service.ts
server/controllers/api/search.ts
server/helpers/webfinger.ts
server/initializers/constants.ts
server/lib/activitypub/actor.ts
server/models/activitypub/actor-follow.ts
server/models/activitypub/actor.ts
server/tests/api/check-params/search.ts
server/tests/api/search/index.ts
server/tests/api/search/search-activitypub-video-channels.ts [new file with mode: 0644]
server/tests/api/search/search-activitypub-videos.ts
server/tests/utils/search/video-channels.ts [new file with mode: 0644]
server/tests/utils/videos/video-channels.ts

index 114a9e517142419263da62828c31ec94e5ff049f..c3ef1d894ba8a5d2bd5cf10ee06f121100ec240d 100644 (file)
@@ -1,6 +1,6 @@
 <div *ngIf="account" class="row">
   <a
-    *ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.name ]"
+    *ngFor="let videoChannel of videoChannels" [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]"
     class="video-channel" i18n-title title="See this video channel"
   >
     <img [src]="videoChannel.avatarUrl" alt="Avatar" />
index 3752de49f662554d7a08d8533ce4db08a1878a6c..2d76990f72493e0150c911e00b5584cd0761983b 100644 (file)
@@ -1,13 +1,13 @@
 <div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()">
   <div *ngFor="let videoChannel of videoChannels" class="video-channel">
-    <a [routerLink]="[ '/video-channels', videoChannel.name ]">
+    <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]">
       <img [src]="videoChannel.avatarUrl" alt="Avatar" />
     </a>
 
     <div class="video-channel-info">
-      <a [routerLink]="[ '/video-channels', videoChannel.name ]" class="video-channel-names" i18n-title title="Go to the channel">
+      <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Go to the channel">
         <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
-        <div class="video-channel-name">{{ videoChannel.name }}</div>
+        <div class="video-channel-name">{{ videoChannel.nameWithHost }}</div>
       </a>
 
       <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
index 548645a76bc2b34ff1e2367a4dcf0a8bcea41f36..df74b19b66a009aeb526dbd7513c9c131e527736 100644 (file)
@@ -7,14 +7,14 @@
 
 <div class="video-channels">
   <div *ngFor="let videoChannel of videoChannels" class="video-channel">
-    <a [routerLink]="[ '/video-channels', videoChannel.name ]">
+    <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]">
       <img [src]="videoChannel.avatarUrl" alt="Avatar" />
     </a>
 
     <div class="video-channel-info">
-      <a [routerLink]="[ '/video-channels', videoChannel.name ]" class="video-channel-names" i18n-title title="Go to the channel">
+      <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Go to the channel">
         <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
-        <div class="video-channel-name">{{ videoChannel.name }}</div>
+        <div class="video-channel-name">{{ videoChannel.nameWithHost }}</div>
       </a>
 
       <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
@@ -23,7 +23,7 @@
     <div class="video-channel-buttons">
       <my-delete-button (click)="deleteVideoChannel(videoChannel)"></my-delete-button>
 
-      <my-edit-button [routerLink]="[ 'update', videoChannel.name ]"></my-edit-button>
+      <my-edit-button [routerLink]="[ 'update', videoChannel.nameWithHost ]"></my-edit-button>
     </div>
   </div>
 </div>
index a63b1ec06f5ac74e08d8d68f8d10c2d1cedea1cb..711b1839da3a583e8fba7578d317c166db5f2889 100644 (file)
   .actor-name {
     flex-grow: 1;
   }
-
-  my-subscribe-button {
-    /deep/ span[role=button] {
-      padding: 7px 12px;
-      font-size: 16px;
-    }
-  }
 }
\ No newline at end of file
index 83d014987321392495796220f0d4533aaa732793..0d09ebbe64fffb27c3c0cfc76e96bda0d75ac330 100644 (file)
   </div>
 
   <div *ngFor="let videoChannel of videoChannels" class="entry video-channel">
-    <a [routerLink]="[ '/video-channels', videoChannel.name ]">
+    <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]">
       <img [src]="videoChannel.avatarUrl" alt="Avatar" />
     </a>
 
     <div class="video-channel-info">
-      <a [routerLink]="[ '/video-channels', videoChannel.name ]" class="video-channel-names">
+      <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names">
         <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
-        <div class="video-channel-name">{{ videoChannel.name }}</div>
+        <div class="video-channel-name">{{ videoChannel.nameWithHost }}</div>
       </a>
 
       <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
index 1a934c8e2dd0c48de20b77b13b5f7295d9e5667b..558db95431119c5e4a69daf5242bbeec07d31b47 100644 (file)
@@ -4,14 +4,7 @@ import { Injectable } from '@angular/core'
 import { Observable } from 'rxjs'
 import { Video as VideoServerModel, VideoDetails as VideoDetailsServerModel } from '../../../../../shared'
 import { ResultList } from '../../../../../shared/models/result-list.model'
-import {
-  UserVideoRate,
-  UserVideoRateUpdate,
-  VideoChannel,
-  VideoFilter,
-  VideoRateType,
-  VideoUpdate
-} from '../../../../../shared/models/videos'
+import { UserVideoRate, UserVideoRateUpdate, VideoFilter, VideoRateType, VideoUpdate } from '../../../../../shared/models/videos'
 import { FeedFormat } from '../../../../../shared/models/feeds/feed-format.enum'
 import { environment } from '../../../environments/environment'
 import { ComponentPagination } from '../rest/component-pagination.model'
@@ -28,6 +21,7 @@ import { AccountService } from '@app/shared/account/account.service'
 import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
 import { ServerService } from '@app/core'
 import { UserSubscriptionService } from '@app/shared/user-subscription'
+import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
 
 @Injectable()
 export class VideoService {
@@ -151,7 +145,7 @@ export class VideoService {
     params = this.restService.addRestGetParams(params, pagination, sort)
 
     return this.authHttp
-               .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.name + '/videos', { params })
+               .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/videos', { params })
                .pipe(
                  switchMap(res => this.extractVideos(res)),
                  catchError(err => this.restExtractor.handleError(err))
index 87aa5d76ff387836a08109c47a951a313846f06d..959d7985570be9bd4f522059728df2571f7fbaf3 100644 (file)
@@ -41,7 +41,6 @@ searchRouter.get('/video-channels',
   videoChannelsSearchSortValidator,
   setDefaultSearchSort,
   optionalAuthenticate,
-  commonVideosFiltersValidator,
   videoChannelsSearchValidator,
   asyncMiddleware(searchVideoChannels)
 )
@@ -59,9 +58,9 @@ function searchVideoChannels (req: express.Request, res: express.Response) {
   const isURISearch = search.startsWith('http://') || search.startsWith('https://')
 
   const parts = search.split('@')
-  const isHandleSearch = parts.length === 2 && parts.every(p => p.indexOf(' ') === -1)
+  const isWebfingerSearch = parts.length === 2 && parts.every(p => p.indexOf(' ') === -1)
 
-  if (isURISearch || isHandleSearch) return searchVideoChannelURI(search, isHandleSearch, res)
+  if (isURISearch || isWebfingerSearch) return searchVideoChannelURI(search, isWebfingerSearch, res)
 
   return searchVideoChannelsDB(query, res)
 }
@@ -81,17 +80,21 @@ async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: expr
   return res.json(getFormattedObjects(resultList.data, resultList.total))
 }
 
-async function searchVideoChannelURI (search: string, isHandleSearch: boolean, res: express.Response) {
+async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) {
   let videoChannel: VideoChannelModel
+  let uri = search
 
-  if (isUserAbleToSearchRemoteURI(res)) {
-    let uri = search
-    if (isHandleSearch) uri = await loadActorUrlOrGetFromWebfinger(search)
+  if (isWebfingerSearch) uri = await loadActorUrlOrGetFromWebfinger(search)
 
-    const actor = await getOrCreateActorAndServerAndModel(uri)
-    videoChannel = actor.VideoChannel
+  if (isUserAbleToSearchRemoteURI(res)) {
+    try {
+      const actor = await getOrCreateActorAndServerAndModel(uri)
+      videoChannel = actor.VideoChannel
+    } catch (err) {
+      logger.info('Cannot search remote video channel %s.', uri, { err })
+    }
   } else {
-    videoChannel = await VideoChannelModel.loadByUrlAndPopulateAccount(search)
+    videoChannel = await VideoChannelModel.loadByUrlAndPopulateAccount(uri)
   }
 
   return res.json({
@@ -138,7 +141,7 @@ async function searchVideoURI (url: string, res: express.Response) {
       const result = await getOrCreateVideoAndAccountAndChannel(url, syncParam)
       video = result ? result.video : undefined
     } catch (err) {
-      logger.info('Cannot search remote video %s.', url)
+      logger.info('Cannot search remote video %s.', url, { err })
     }
   } else {
     video = await VideoModel.loadByUrlAndPopulateAccount(url)
index 5c60de10c2cca88254f5a4c10dfefe1f7173beb6..10fcec4622c9f1b6429cd0a048d6d28ab31cd1bc 100644 (file)
@@ -3,6 +3,7 @@ import { WebFingerData } from '../../shared'
 import { ActorModel } from '../models/activitypub/actor'
 import { isTestInstance } from './core-utils'
 import { isActivityPubUrlValid } from './custom-validators/activitypub/misc'
+import { CONFIG } from '../initializers'
 
 const webfinger = new WebFinger({
   webfist_fallback: false,
@@ -13,8 +14,14 @@ const webfinger = new WebFinger({
 
 async function loadActorUrlOrGetFromWebfinger (uri: string) {
   const [ name, host ] = uri.split('@')
+  let actor: ActorModel
+
+  if (host === CONFIG.WEBSERVER.HOST) {
+    actor = await ActorModel.loadLocalByName(name)
+  } else {
+    actor = await ActorModel.loadByNameAndHost(name, host)
+  }
 
-  const actor = await ActorModel.loadByNameAndHost(name, host)
   if (actor) return actor.url
 
   return getUrlFromWebfinger(uri)
index 9beb9b7c242741388d09085c12babff0f0646265..a0dd78f42089d76f62138a75398360defa7a5295 100644 (file)
@@ -44,7 +44,7 @@ const SORTABLE_COLUMNS = {
   FOLLOWING: [ 'createdAt' ],
 
   VIDEOS_SEARCH: [ 'match', 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes' ],
-  VIDEO_CHANNELS_SEARCH: [ 'match', 'displayName' ]
+  VIDEO_CHANNELS_SEARCH: [ 'match', 'displayName', 'createdAt' ]
 }
 
 const OAUTH_LIFETIME = {
index 9922229d2b26e38ea010a2b73c68c22777be1910..22e1c9f1950059ba768613a99a78c150740297af 100644 (file)
@@ -48,7 +48,7 @@ async function getOrCreateActorAndServerAndModel (activityActor: string | Activi
 
   // We don't have this actor in our database, fetch it on remote
   if (!actor) {
-    const result = await fetchRemoteActor(actorUrl)
+    const { result } = await fetchRemoteActor(actorUrl)
     if (result === undefined) throw new Error('Cannot fetch remote actor.')
 
     // Create the attributed to actor
@@ -70,7 +70,13 @@ async function getOrCreateActorAndServerAndModel (activityActor: string | Activi
     actor = await retryTransactionWrapper(saveActorAndServerAndModelIfNotExist, result, ownerActor)
   }
 
-  return retryTransactionWrapper(refreshActorIfNeeded, actor)
+  if (actor.Account) actor.Account.Actor = actor
+  if (actor.VideoChannel) actor.VideoChannel.Actor = actor
+
+  actor = await retryTransactionWrapper(refreshActorIfNeeded, actor)
+  if (!actor) throw new Error('Actor ' + actor.url + ' does not exist anymore.')
+
+  return actor
 }
 
 function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string, uuid?: string) {
@@ -264,7 +270,7 @@ type FetchRemoteActorResult = {
   avatarName?: string
   attributedTo: ActivityPubAttributedTo[]
 }
-async function fetchRemoteActor (actorUrl: string): Promise<FetchRemoteActorResult> {
+async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: number, result: FetchRemoteActorResult }> {
   const options = {
     uri: actorUrl,
     method: 'GET',
@@ -281,7 +287,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<FetchRemoteActorResu
 
   if (isActorObjectValid(actorJSON) === false) {
     logger.debug('Remote actor JSON is not valid.', { actorJSON: actorJSON })
-    return undefined
+    return { result: undefined, statusCode: requestResult.response.statusCode }
   }
 
   const followersCount = await fetchActorTotalItems(actorJSON.followers)
@@ -307,12 +313,15 @@ async function fetchRemoteActor (actorUrl: string): Promise<FetchRemoteActorResu
 
   const name = actorJSON.name || actorJSON.preferredUsername
   return {
-    actor,
-    name,
-    avatarName,
-    summary: actorJSON.summary,
-    support: actorJSON.support,
-    attributedTo: actorJSON.attributedTo
+    statusCode: requestResult.response.statusCode,
+    result: {
+      actor,
+      name,
+      avatarName,
+      summary: actorJSON.summary,
+      support: actorJSON.support,
+      attributedTo: actorJSON.attributedTo
+    }
   }
 }
 
@@ -355,7 +364,14 @@ async function refreshActorIfNeeded (actor: ActorModel): Promise<ActorModel> {
 
   try {
     const actorUrl = await getUrlFromWebfinger(actor.preferredUsername + '@' + actor.getHost())
-    const result = await fetchRemoteActor(actorUrl)
+    const { result, statusCode } = await fetchRemoteActor(actorUrl)
+
+    if (statusCode === 404) {
+      logger.info('Deleting actor %s because there is a 404 in refresh actor.', actor.url)
+      actor.Account ? actor.Account.destroy() : actor.VideoChannel.destroy()
+      return undefined
+    }
+
     if (result === undefined) {
       logger.warn('Cannot fetch remote actor in refresh actor.')
       return actor
index ebb2d47c263bba185c82220ef514e98de3194683..8bc09599747608c6520b07c01ce55cc716f736aa 100644 (file)
@@ -325,15 +325,13 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
       },
       include: [
         {
-          attributes: {
-            exclude: unusedActorAttributesForAPI
-          },
-          model: ActorModel,
+          attributes: [ 'id' ],
+          model: ActorModel.unscoped(),
           as: 'ActorFollowing',
           required: true,
           include: [
             {
-              model: VideoChannelModel,
+              model: VideoChannelModel.unscoped(),
               required: true,
               include: [
                 {
@@ -344,7 +342,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
                   required: true
                 },
                 {
-                  model: AccountModel,
+                  model: AccountModel.unscoped(),
                   required: true,
                   include: [
                     {
index e16bd5d7943de0b36e55cba50e56c64ea58e7516..119d0c1da1bba13c2a09c5afc2643975f78b5ab9 100644 (file)
@@ -50,7 +50,9 @@ export const unusedActorAttributesForAPI = [
   'sharedInboxUrl',
   'followersUrl',
   'followingUrl',
-  'url'
+  'url',
+  'createdAt',
+  'updatedAt'
 ]
 
 @DefaultScope({
index d35eac7fe2498d8c1e93b62366e98ba6ceeb91fa..eabf602acffb601200708631e16341d6b1ac36f1 100644 (file)
@@ -6,7 +6,6 @@ import { flushTests, immutableAssign, killallServers, makeGetRequest, runServer,
 import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
 
 describe('Test videos API validator', function () {
-  const path = '/api/v1/search/videos/'
   let server: ServerInfo
 
   // ---------------------------------------------------------------
@@ -20,6 +19,8 @@ describe('Test videos API validator', function () {
   })
 
   describe('When searching videos', function () {
+    const path = '/api/v1/search/videos/'
+
     const query = {
       search: 'coucou'
     }
@@ -111,6 +112,30 @@ describe('Test videos API validator', function () {
     })
   })
 
+  describe('When searching video channels', function () {
+    const path = '/api/v1/search/video-channels/'
+
+    const query = {
+      search: 'coucou'
+    }
+
+    it('Should fail with a bad start pagination', async function () {
+      await checkBadStartPagination(server.url, path, null, query)
+    })
+
+    it('Should fail with a bad count pagination', async function () {
+      await checkBadCountPagination(server.url, path, null, query)
+    })
+
+    it('Should fail with an incorrect sort', async function () {
+      await checkBadSortPagination(server.url, path, null, query)
+    })
+
+    it('Should success with the correct parameters', async function () {
+      await makeGetRequest({ url: server.url, path, query, statusCodeExpected: 200 })
+    })
+  })
+
   after(async function () {
     killallServers([ server ])
 
index 64b3d0910e8714007a2c807f0791d8455ae9d1f2..d573c8a9e612b00c29600f2835b82ff31889f90a 100644 (file)
@@ -1,2 +1,3 @@
+import './search-activitypub-video-channels'
 import './search-activitypub-videos'
 import './search-videos'
diff --git a/server/tests/api/search/search-activitypub-video-channels.ts b/server/tests/api/search/search-activitypub-video-channels.ts
new file mode 100644 (file)
index 0000000..512cb32
--- /dev/null
@@ -0,0 +1,176 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import {
+  addVideoChannel,
+  createUser,
+  deleteVideoChannel,
+  flushAndRunMultipleServers,
+  flushTests,
+  getVideoChannelsList,
+  killallServers,
+  ServerInfo,
+  setAccessTokensToServers,
+  updateMyUser,
+  updateVideoChannel,
+  uploadVideo,
+  userLogin,
+  wait
+} from '../../utils'
+import { waitJobs } from '../../utils/server/jobs'
+import { VideoChannel } from '../../../../shared/models/videos'
+import { searchVideoChannel } from '../../utils/search/video-channels'
+
+const expect = chai.expect
+
+describe('Test a ActivityPub video channels search', function () {
+  let servers: ServerInfo[]
+  let userServer2Token: string
+
+  before(async function () {
+    this.timeout(120000)
+
+    await flushTests()
+
+    servers = await flushAndRunMultipleServers(2)
+
+    await setAccessTokensToServers(servers)
+
+    {
+      await createUser(servers[0].url, servers[0].accessToken, 'user1_server1', 'password')
+      const channel = {
+        name: 'channel1_server1',
+        displayName: 'Channel 1 server 1'
+      }
+      await addVideoChannel(servers[0].url, servers[0].accessToken, channel)
+    }
+
+    {
+      const user = { username: 'user1_server2', password: 'password' }
+      await createUser(servers[1].url, servers[1].accessToken, user.username, user.password)
+      userServer2Token = await userLogin(servers[1], user)
+
+      const channel = {
+        name: 'channel1_server2',
+        displayName: 'Channel 1 server 2'
+      }
+      const resChannel = await addVideoChannel(servers[1].url, userServer2Token, channel)
+      const channelId = resChannel.body.videoChannel.id
+
+      await uploadVideo(servers[1].url, userServer2Token, { name: 'video 1 server 2', channelId })
+      await uploadVideo(servers[1].url, userServer2Token, { name: 'video 2 server 2', channelId })
+    }
+
+    await waitJobs(servers)
+  })
+
+  it('Should not find a remote video channel', async function () {
+    {
+      const search = 'http://localhost:9002/video-channels/channel1_server3'
+      const res = await searchVideoChannel(servers[ 0 ].url, search, servers[ 0 ].accessToken)
+
+      expect(res.body.total).to.equal(0)
+      expect(res.body.data).to.be.an('array')
+      expect(res.body.data).to.have.lengthOf(0)
+    }
+
+    {
+      // Without token
+      const search = 'http://localhost:9002/video-channels/channel1_server2'
+      const res = await searchVideoChannel(servers[0].url, search)
+
+      expect(res.body.total).to.equal(0)
+      expect(res.body.data).to.be.an('array')
+      expect(res.body.data).to.have.lengthOf(0)
+    }
+  })
+
+  it('Should search a local video channel', async function () {
+    const searches = [
+      'http://localhost:9001/video-channels/channel1_server1',
+      'channel1_server1@localhost:9001'
+    ]
+
+    for (const search of searches) {
+      const res = await searchVideoChannel(servers[ 0 ].url, search)
+
+      expect(res.body.total).to.equal(1)
+      expect(res.body.data).to.be.an('array')
+      expect(res.body.data).to.have.lengthOf(1)
+      expect(res.body.data[ 0 ].name).to.equal('channel1_server1')
+      expect(res.body.data[ 0 ].displayName).to.equal('Channel 1 server 1')
+    }
+  })
+
+  it('Should search a remote video channel with URL or handle', async function () {
+    const searches = [
+      'http://localhost:9002/video-channels/channel1_server2',
+      'channel1_server2@localhost:9002'
+    ]
+
+    for (const search of searches) {
+      const res = await searchVideoChannel(servers[ 0 ].url, search, servers[ 0 ].accessToken)
+
+      expect(res.body.total).to.equal(1)
+      expect(res.body.data).to.be.an('array')
+      expect(res.body.data).to.have.lengthOf(1)
+      expect(res.body.data[ 0 ].name).to.equal('channel1_server2')
+      expect(res.body.data[ 0 ].displayName).to.equal('Channel 1 server 2')
+    }
+  })
+
+  it('Should not list this remote video channel', async function () {
+    const res = await getVideoChannelsList(servers[0].url, 0, 5)
+    expect(res.body.total).to.equal(3)
+    expect(res.body.data).to.have.lengthOf(3)
+    expect(res.body.data[0].name).to.equal('channel1_server1')
+    expect(res.body.data[1].name).to.equal('user1_server1_channel')
+    expect(res.body.data[2].name).to.equal('root_channel')
+  })
+
+  it('Should update video channel of server 2, and refresh it on server 1', async function () {
+    this.timeout(60000)
+
+    await updateVideoChannel(servers[1].url, userServer2Token, 'channel1_server2', { displayName: 'channel updated' })
+    await updateMyUser({ url: servers[1].url, accessToken: userServer2Token, displayName: 'user updated' })
+
+    await waitJobs(servers)
+    // Expire video channel
+    await wait(10000)
+
+    const search = 'http://localhost:9002/video-channels/channel1_server2'
+    const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken)
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.have.lengthOf(1)
+
+    const videoChannel: VideoChannel = res.body.data[0]
+    expect(videoChannel.displayName).to.equal('channel updated')
+
+    // We don't return the owner account for now
+    // expect(videoChannel.ownerAccount.displayName).to.equal('user updated')
+  })
+
+  it('Should delete video channel of server 2, and delete it on server 1', async function () {
+    this.timeout(60000)
+
+    await deleteVideoChannel(servers[1].url, userServer2Token, 'channel1_server2')
+
+    await waitJobs(servers)
+    // Expire video
+    await wait(10000)
+
+    const res = await searchVideoChannel(servers[0].url, 'http://localhost:9002/video-channels/channel1_server2', servers[0].accessToken)
+    expect(res.body.total).to.equal(0)
+    expect(res.body.data).to.have.lengthOf(0)
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
index 6dc792696eaae432d111d074d6fe89b566f7d5e4..28f4fac50d2c680c346182e03d212f9a16cbf6b1 100644 (file)
@@ -59,6 +59,7 @@ describe('Test a ActivityPub videos search', function () {
     }
 
     {
+      // Without token
       const res = await searchVideo(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID)
 
       expect(res.body.total).to.equal(0)
diff --git a/server/tests/utils/search/video-channels.ts b/server/tests/utils/search/video-channels.ts
new file mode 100644 (file)
index 0000000..0532134
--- /dev/null
@@ -0,0 +1,22 @@
+import { makeGetRequest } from '../requests/requests'
+
+function searchVideoChannel (url: string, search: string, token?: string, statusCodeExpected = 200) {
+  const path = '/api/v1/search/video-channels'
+
+  return makeGetRequest({
+    url,
+    path,
+    query: {
+      sort: '-createdAt',
+      search
+    },
+    token,
+    statusCodeExpected
+  })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  searchVideoChannel
+}
index 1eea22b31d865f0854d20c070ab62afe6aa4964f..09298577798ff98eb69ba547ff62b0a6e8d58d2d 100644 (file)
@@ -54,12 +54,12 @@ function addVideoChannel (
 function updateVideoChannel (
   url: string,
   token: string,
-  channelId: number | string,
+  channelName: string,
   attributes: VideoChannelUpdate,
   expectedStatus = 204
 ) {
   const body = {}
-  const path = '/api/v1/video-channels/' + channelId
+  const path = '/api/v1/video-channels/' + channelName
 
   if (attributes.displayName) body['displayName'] = attributes.displayName
   if (attributes.description) body['description'] = attributes.description
@@ -73,8 +73,8 @@ function updateVideoChannel (
     .expect(expectedStatus)
 }
 
-function deleteVideoChannel (url: string, token: string, channelId: number | string, expectedStatus = 204) {
-  const path = '/api/v1/video-channels/' + channelId
+function deleteVideoChannel (url: string, token: string, channelName: string, expectedStatus = 204) {
+  const path = '/api/v1/video-channels/' + channelName
 
   return request(url)
     .delete(path)
@@ -83,8 +83,8 @@ function deleteVideoChannel (url: string, token: string, channelId: number | str
     .expect(expectedStatus)
 }
 
-function getVideoChannel (url: string, channelId: number | string) {
-  const path = '/api/v1/video-channels/' + channelId
+function getVideoChannel (url: string, channelName: string) {
+  const path = '/api/v1/video-channels/' + channelName
 
   return request(url)
     .get(path)