aboutsummaryrefslogtreecommitdiffhomepage
path: root/packages/tests/src/api/search
diff options
context:
space:
mode:
Diffstat (limited to 'packages/tests/src/api/search')
-rw-r--r--packages/tests/src/api/search/index.ts7
-rw-r--r--packages/tests/src/api/search/search-activitypub-video-channels.ts255
-rw-r--r--packages/tests/src/api/search/search-activitypub-video-playlists.ts214
-rw-r--r--packages/tests/src/api/search/search-activitypub-videos.ts196
-rw-r--r--packages/tests/src/api/search/search-channels.ts159
-rw-r--r--packages/tests/src/api/search/search-index.ts438
-rw-r--r--packages/tests/src/api/search/search-playlists.ts180
-rw-r--r--packages/tests/src/api/search/search-videos.ts568
8 files changed, 2017 insertions, 0 deletions
diff --git a/packages/tests/src/api/search/index.ts b/packages/tests/src/api/search/index.ts
new file mode 100644
index 000000000..f4420261d
--- /dev/null
+++ b/packages/tests/src/api/search/index.ts
@@ -0,0 +1,7 @@
1import './search-activitypub-video-playlists.js'
2import './search-activitypub-video-channels.js'
3import './search-activitypub-videos.js'
4import './search-channels.js'
5import './search-index.js'
6import './search-playlists.js'
7import './search-videos.js'
diff --git a/packages/tests/src/api/search/search-activitypub-video-channels.ts b/packages/tests/src/api/search/search-activitypub-video-channels.ts
new file mode 100644
index 000000000..b63f45b18
--- /dev/null
+++ b/packages/tests/src/api/search/search-activitypub-video-channels.ts
@@ -0,0 +1,255 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { wait } from '@peertube/peertube-core-utils'
5import { VideoChannel } from '@peertube/peertube-models'
6import {
7 cleanupTests,
8 createMultipleServers,
9 PeerTubeServer,
10 SearchCommand,
11 setAccessTokensToServers,
12 setDefaultAccountAvatar,
13 setDefaultVideoChannel,
14 waitJobs
15} from '@peertube/peertube-server-commands'
16
17describe('Test ActivityPub video channels search', function () {
18 let servers: PeerTubeServer[]
19 let userServer2Token: string
20 let videoServer2UUID: string
21 let channelIdServer2: number
22 let command: SearchCommand
23
24 before(async function () {
25 this.timeout(120000)
26
27 servers = await createMultipleServers(2)
28
29 await setAccessTokensToServers(servers)
30 await setDefaultVideoChannel(servers)
31 await setDefaultAccountAvatar(servers)
32
33 {
34 await servers[0].users.create({ username: 'user1_server1', password: 'password' })
35 const channel = {
36 name: 'channel1_server1',
37 displayName: 'Channel 1 server 1'
38 }
39 await servers[0].channels.create({ attributes: channel })
40 }
41
42 {
43 const user = { username: 'user1_server2', password: 'password' }
44 await servers[1].users.create({ username: user.username, password: user.password })
45 userServer2Token = await servers[1].login.getAccessToken(user)
46
47 const channel = {
48 name: 'channel1_server2',
49 displayName: 'Channel 1 server 2'
50 }
51 const created = await servers[1].channels.create({ token: userServer2Token, attributes: channel })
52 channelIdServer2 = created.id
53
54 const attributes = { name: 'video 1 server 2', channelId: channelIdServer2 }
55 const { uuid } = await servers[1].videos.upload({ token: userServer2Token, attributes })
56 videoServer2UUID = uuid
57 }
58
59 await waitJobs(servers)
60
61 command = servers[0].search
62 })
63
64 it('Should not find a remote video channel', async function () {
65 this.timeout(15000)
66
67 {
68 const search = servers[1].url + '/video-channels/channel1_server3'
69 const body = await command.searchChannels({ search, token: servers[0].accessToken })
70
71 expect(body.total).to.equal(0)
72 expect(body.data).to.be.an('array')
73 expect(body.data).to.have.lengthOf(0)
74 }
75
76 {
77 // Without token
78 const search = servers[1].url + '/video-channels/channel1_server2'
79 const body = await command.searchChannels({ search })
80
81 expect(body.total).to.equal(0)
82 expect(body.data).to.be.an('array')
83 expect(body.data).to.have.lengthOf(0)
84 }
85 })
86
87 it('Should search a local video channel', async function () {
88 const searches = [
89 servers[0].url + '/video-channels/channel1_server1',
90 'channel1_server1@' + servers[0].host
91 ]
92
93 for (const search of searches) {
94 const body = await command.searchChannels({ search })
95
96 expect(body.total).to.equal(1)
97 expect(body.data).to.be.an('array')
98 expect(body.data).to.have.lengthOf(1)
99 expect(body.data[0].name).to.equal('channel1_server1')
100 expect(body.data[0].displayName).to.equal('Channel 1 server 1')
101 }
102 })
103
104 it('Should search a local video channel with an alternative URL', async function () {
105 const search = servers[0].url + '/c/channel1_server1'
106
107 for (const token of [ undefined, servers[0].accessToken ]) {
108 const body = await command.searchChannels({ search, token })
109
110 expect(body.total).to.equal(1)
111 expect(body.data).to.be.an('array')
112 expect(body.data).to.have.lengthOf(1)
113 expect(body.data[0].name).to.equal('channel1_server1')
114 expect(body.data[0].displayName).to.equal('Channel 1 server 1')
115 }
116 })
117
118 it('Should search a local video channel with a query in URL', async function () {
119 const searches = [
120 servers[0].url + '/video-channels/channel1_server1',
121 servers[0].url + '/c/channel1_server1'
122 ]
123
124 for (const search of searches) {
125 for (const token of [ undefined, servers[0].accessToken ]) {
126 const body = await command.searchChannels({ search: search + '?param=2', token })
127
128 expect(body.total).to.equal(1)
129 expect(body.data).to.be.an('array')
130 expect(body.data).to.have.lengthOf(1)
131 expect(body.data[0].name).to.equal('channel1_server1')
132 expect(body.data[0].displayName).to.equal('Channel 1 server 1')
133 }
134 }
135 })
136
137 it('Should search a remote video channel with URL or handle', async function () {
138 const searches = [
139 servers[1].url + '/video-channels/channel1_server2',
140 servers[1].url + '/c/channel1_server2',
141 servers[1].url + '/c/channel1_server2/videos',
142 'channel1_server2@' + servers[1].host
143 ]
144
145 for (const search of searches) {
146 const body = await command.searchChannels({ search, token: servers[0].accessToken })
147
148 expect(body.total).to.equal(1)
149 expect(body.data).to.be.an('array')
150 expect(body.data).to.have.lengthOf(1)
151 expect(body.data[0].name).to.equal('channel1_server2')
152 expect(body.data[0].displayName).to.equal('Channel 1 server 2')
153 }
154 })
155
156 it('Should not list this remote video channel', async function () {
157 const body = await servers[0].channels.list()
158 expect(body.total).to.equal(3)
159 expect(body.data).to.have.lengthOf(3)
160 expect(body.data[0].name).to.equal('channel1_server1')
161 expect(body.data[1].name).to.equal('user1_server1_channel')
162 expect(body.data[2].name).to.equal('root_channel')
163 })
164
165 it('Should list video channel videos of server 2 without token', async function () {
166 this.timeout(30000)
167
168 await waitJobs(servers)
169
170 const { total, data } = await servers[0].videos.listByChannel({
171 token: null,
172 handle: 'channel1_server2@' + servers[1].host
173 })
174 expect(total).to.equal(0)
175 expect(data).to.have.lengthOf(0)
176 })
177
178 it('Should list video channel videos of server 2 with token', async function () {
179 const { total, data } = await servers[0].videos.listByChannel({
180 handle: 'channel1_server2@' + servers[1].host
181 })
182
183 expect(total).to.equal(1)
184 expect(data[0].name).to.equal('video 1 server 2')
185 })
186
187 it('Should update video channel of server 2, and refresh it on server 1', async function () {
188 this.timeout(120000)
189
190 await servers[1].channels.update({
191 token: userServer2Token,
192 channelName: 'channel1_server2',
193 attributes: { displayName: 'channel updated' }
194 })
195 await servers[1].users.updateMe({ token: userServer2Token, displayName: 'user updated' })
196
197 await waitJobs(servers)
198 // Expire video channel
199 await wait(10000)
200
201 const search = servers[1].url + '/video-channels/channel1_server2'
202 const body = await command.searchChannels({ search, token: servers[0].accessToken })
203 expect(body.total).to.equal(1)
204 expect(body.data).to.have.lengthOf(1)
205
206 const videoChannel: VideoChannel = body.data[0]
207 expect(videoChannel.displayName).to.equal('channel updated')
208
209 // We don't return the owner account for now
210 // expect(videoChannel.ownerAccount.displayName).to.equal('user updated')
211 })
212
213 it('Should update and add a video on server 2, and update it on server 1 after a search', async function () {
214 this.timeout(120000)
215
216 await servers[1].videos.update({ token: userServer2Token, id: videoServer2UUID, attributes: { name: 'video 1 updated' } })
217 await servers[1].videos.upload({ token: userServer2Token, attributes: { name: 'video 2 server 2', channelId: channelIdServer2 } })
218
219 await waitJobs(servers)
220
221 // Expire video channel
222 await wait(10000)
223
224 const search = servers[1].url + '/video-channels/channel1_server2'
225 await command.searchChannels({ search, token: servers[0].accessToken })
226
227 await waitJobs(servers)
228
229 const handle = 'channel1_server2@' + servers[1].host
230 const { total, data } = await servers[0].videos.listByChannel({ handle, sort: '-createdAt' })
231
232 expect(total).to.equal(2)
233 expect(data[0].name).to.equal('video 2 server 2')
234 expect(data[1].name).to.equal('video 1 updated')
235 })
236
237 it('Should delete video channel of server 2, and delete it on server 1', async function () {
238 this.timeout(120000)
239
240 await servers[1].channels.delete({ token: userServer2Token, channelName: 'channel1_server2' })
241
242 await waitJobs(servers)
243 // Expire video
244 await wait(10000)
245
246 const search = servers[1].url + '/video-channels/channel1_server2'
247 const body = await command.searchChannels({ search, token: servers[0].accessToken })
248 expect(body.total).to.equal(0)
249 expect(body.data).to.have.lengthOf(0)
250 })
251
252 after(async function () {
253 await cleanupTests(servers)
254 })
255})
diff --git a/packages/tests/src/api/search/search-activitypub-video-playlists.ts b/packages/tests/src/api/search/search-activitypub-video-playlists.ts
new file mode 100644
index 000000000..33ecfd8e7
--- /dev/null
+++ b/packages/tests/src/api/search/search-activitypub-video-playlists.ts
@@ -0,0 +1,214 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { wait } from '@peertube/peertube-core-utils'
5import { VideoPlaylistPrivacy } from '@peertube/peertube-models'
6import {
7 cleanupTests,
8 createMultipleServers,
9 PeerTubeServer,
10 SearchCommand,
11 setAccessTokensToServers,
12 setDefaultAccountAvatar,
13 setDefaultVideoChannel,
14 waitJobs
15} from '@peertube/peertube-server-commands'
16
17describe('Test ActivityPub playlists search', function () {
18 let servers: PeerTubeServer[]
19 let playlistServer1UUID: string
20 let playlistServer2UUID: string
21 let video2Server2: string
22
23 let command: SearchCommand
24
25 before(async function () {
26 this.timeout(240000)
27
28 servers = await createMultipleServers(2)
29
30 await setAccessTokensToServers(servers)
31 await setDefaultVideoChannel(servers)
32 await setDefaultAccountAvatar(servers)
33
34 {
35 const video1 = (await servers[0].videos.quickUpload({ name: 'video 1' })).uuid
36 const video2 = (await servers[0].videos.quickUpload({ name: 'video 2' })).uuid
37
38 const attributes = {
39 displayName: 'playlist 1 on server 1',
40 privacy: VideoPlaylistPrivacy.PUBLIC,
41 videoChannelId: servers[0].store.channel.id
42 }
43 const created = await servers[0].playlists.create({ attributes })
44 playlistServer1UUID = created.uuid
45
46 for (const videoId of [ video1, video2 ]) {
47 await servers[0].playlists.addElement({ playlistId: playlistServer1UUID, attributes: { videoId } })
48 }
49 }
50
51 {
52 const videoId = (await servers[1].videos.quickUpload({ name: 'video 1' })).uuid
53 video2Server2 = (await servers[1].videos.quickUpload({ name: 'video 2' })).uuid
54
55 const attributes = {
56 displayName: 'playlist 1 on server 2',
57 privacy: VideoPlaylistPrivacy.PUBLIC,
58 videoChannelId: servers[1].store.channel.id
59 }
60 const created = await servers[1].playlists.create({ attributes })
61 playlistServer2UUID = created.uuid
62
63 await servers[1].playlists.addElement({ playlistId: playlistServer2UUID, attributes: { videoId } })
64 }
65
66 await waitJobs(servers)
67
68 command = servers[0].search
69 })
70
71 it('Should not find a remote playlist', async function () {
72 {
73 const search = servers[1].url + '/video-playlists/43'
74 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
75
76 expect(body.total).to.equal(0)
77 expect(body.data).to.be.an('array')
78 expect(body.data).to.have.lengthOf(0)
79 }
80
81 {
82 // Without token
83 const search = servers[1].url + '/video-playlists/' + playlistServer2UUID
84 const body = await command.searchPlaylists({ search })
85
86 expect(body.total).to.equal(0)
87 expect(body.data).to.be.an('array')
88 expect(body.data).to.have.lengthOf(0)
89 }
90 })
91
92 it('Should search a local playlist', async function () {
93 const search = servers[0].url + '/video-playlists/' + playlistServer1UUID
94 const body = await command.searchPlaylists({ search })
95
96 expect(body.total).to.equal(1)
97 expect(body.data).to.be.an('array')
98 expect(body.data).to.have.lengthOf(1)
99 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
100 expect(body.data[0].videosLength).to.equal(2)
101 })
102
103 it('Should search a local playlist with an alternative URL', async function () {
104 const searches = [
105 servers[0].url + '/videos/watch/playlist/' + playlistServer1UUID,
106 servers[0].url + '/w/p/' + playlistServer1UUID
107 ]
108
109 for (const search of searches) {
110 for (const token of [ undefined, servers[0].accessToken ]) {
111 const body = await command.searchPlaylists({ search, token })
112
113 expect(body.total).to.equal(1)
114 expect(body.data).to.be.an('array')
115 expect(body.data).to.have.lengthOf(1)
116 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
117 expect(body.data[0].videosLength).to.equal(2)
118 }
119 }
120 })
121
122 it('Should search a local playlist with a query in URL', async function () {
123 const searches = [
124 servers[0].url + '/videos/watch/playlist/' + playlistServer1UUID,
125 servers[0].url + '/w/p/' + playlistServer1UUID
126 ]
127
128 for (const search of searches) {
129 for (const token of [ undefined, servers[0].accessToken ]) {
130 const body = await command.searchPlaylists({ search: search + '?param=1', token })
131
132 expect(body.total).to.equal(1)
133 expect(body.data).to.be.an('array')
134 expect(body.data).to.have.lengthOf(1)
135 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
136 expect(body.data[0].videosLength).to.equal(2)
137 }
138 }
139 })
140
141 it('Should search a remote playlist', async function () {
142 const searches = [
143 servers[1].url + '/video-playlists/' + playlistServer2UUID,
144 servers[1].url + '/videos/watch/playlist/' + playlistServer2UUID,
145 servers[1].url + '/w/p/' + playlistServer2UUID
146 ]
147
148 for (const search of searches) {
149 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
150
151 expect(body.total).to.equal(1)
152 expect(body.data).to.be.an('array')
153 expect(body.data).to.have.lengthOf(1)
154 expect(body.data[0].displayName).to.equal('playlist 1 on server 2')
155 expect(body.data[0].videosLength).to.equal(1)
156 }
157 })
158
159 it('Should not list this remote playlist', async function () {
160 const body = await servers[0].playlists.list({ start: 0, count: 10 })
161 expect(body.total).to.equal(1)
162 expect(body.data).to.have.lengthOf(1)
163 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
164 })
165
166 it('Should update the playlist of server 2, and refresh it on server 1', async function () {
167 this.timeout(60000)
168
169 await servers[1].playlists.addElement({ playlistId: playlistServer2UUID, attributes: { videoId: video2Server2 } })
170
171 await waitJobs(servers)
172 // Expire playlist
173 await wait(10000)
174
175 // Will run refresh async
176 const search = servers[1].url + '/video-playlists/' + playlistServer2UUID
177 await command.searchPlaylists({ search, token: servers[0].accessToken })
178
179 // Wait refresh
180 await wait(5000)
181
182 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
183 expect(body.total).to.equal(1)
184 expect(body.data).to.have.lengthOf(1)
185
186 const playlist = body.data[0]
187 expect(playlist.videosLength).to.equal(2)
188 })
189
190 it('Should delete playlist of server 2, and delete it on server 1', async function () {
191 this.timeout(60000)
192
193 await servers[1].playlists.delete({ playlistId: playlistServer2UUID })
194
195 await waitJobs(servers)
196 // Expiration
197 await wait(10000)
198
199 // Will run refresh async
200 const search = servers[1].url + '/video-playlists/' + playlistServer2UUID
201 await command.searchPlaylists({ search, token: servers[0].accessToken })
202
203 // Wait refresh
204 await wait(5000)
205
206 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
207 expect(body.total).to.equal(0)
208 expect(body.data).to.have.lengthOf(0)
209 })
210
211 after(async function () {
212 await cleanupTests(servers)
213 })
214})
diff --git a/packages/tests/src/api/search/search-activitypub-videos.ts b/packages/tests/src/api/search/search-activitypub-videos.ts
new file mode 100644
index 000000000..72759f21e
--- /dev/null
+++ b/packages/tests/src/api/search/search-activitypub-videos.ts
@@ -0,0 +1,196 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { wait } from '@peertube/peertube-core-utils'
5import { VideoPrivacy } from '@peertube/peertube-models'
6import {
7 cleanupTests,
8 createMultipleServers,
9 PeerTubeServer,
10 SearchCommand,
11 setAccessTokensToServers,
12 setDefaultAccountAvatar,
13 setDefaultVideoChannel,
14 waitJobs
15} from '@peertube/peertube-server-commands'
16
17describe('Test ActivityPub videos search', function () {
18 let servers: PeerTubeServer[]
19 let videoServer1UUID: string
20 let videoServer2UUID: string
21
22 let command: SearchCommand
23
24 before(async function () {
25 this.timeout(120000)
26
27 servers = await createMultipleServers(2)
28
29 await setAccessTokensToServers(servers)
30 await setDefaultVideoChannel(servers)
31 await setDefaultAccountAvatar(servers)
32
33 {
34 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1 on server 1' } })
35 videoServer1UUID = uuid
36 }
37
38 {
39 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 1 on server 2' } })
40 videoServer2UUID = uuid
41 }
42
43 await waitJobs(servers)
44
45 command = servers[0].search
46 })
47
48 it('Should not find a remote video', async function () {
49 {
50 const search = servers[1].url + '/videos/watch/43'
51 const body = await command.searchVideos({ search, token: servers[0].accessToken })
52
53 expect(body.total).to.equal(0)
54 expect(body.data).to.be.an('array')
55 expect(body.data).to.have.lengthOf(0)
56 }
57
58 {
59 // Without token
60 const search = servers[1].url + '/videos/watch/' + videoServer2UUID
61 const body = await command.searchVideos({ search })
62
63 expect(body.total).to.equal(0)
64 expect(body.data).to.be.an('array')
65 expect(body.data).to.have.lengthOf(0)
66 }
67 })
68
69 it('Should search a local video', async function () {
70 const search = servers[0].url + '/videos/watch/' + videoServer1UUID
71 const body = await command.searchVideos({ search })
72
73 expect(body.total).to.equal(1)
74 expect(body.data).to.be.an('array')
75 expect(body.data).to.have.lengthOf(1)
76 expect(body.data[0].name).to.equal('video 1 on server 1')
77 })
78
79 it('Should search a local video with an alternative URL', async function () {
80 const search = servers[0].url + '/w/' + videoServer1UUID
81 const body1 = await command.searchVideos({ search })
82 const body2 = await command.searchVideos({ search, token: servers[0].accessToken })
83
84 for (const body of [ body1, body2 ]) {
85 expect(body.total).to.equal(1)
86 expect(body.data).to.be.an('array')
87 expect(body.data).to.have.lengthOf(1)
88 expect(body.data[0].name).to.equal('video 1 on server 1')
89 }
90 })
91
92 it('Should search a local video with a query in URL', async function () {
93 const searches = [
94 servers[0].url + '/w/' + videoServer1UUID,
95 servers[0].url + '/videos/watch/' + videoServer1UUID
96 ]
97
98 for (const search of searches) {
99 for (const token of [ undefined, servers[0].accessToken ]) {
100 const body = await command.searchVideos({ search: search + '?startTime=4', token })
101
102 expect(body.total).to.equal(1)
103 expect(body.data).to.be.an('array')
104 expect(body.data).to.have.lengthOf(1)
105 expect(body.data[0].name).to.equal('video 1 on server 1')
106 }
107 }
108 })
109
110 it('Should search a remote video', async function () {
111 const searches = [
112 servers[1].url + '/w/' + videoServer2UUID,
113 servers[1].url + '/videos/watch/' + videoServer2UUID
114 ]
115
116 for (const search of searches) {
117 const body = await command.searchVideos({ search, token: servers[0].accessToken })
118
119 expect(body.total).to.equal(1)
120 expect(body.data).to.be.an('array')
121 expect(body.data).to.have.lengthOf(1)
122 expect(body.data[0].name).to.equal('video 1 on server 2')
123 }
124 })
125
126 it('Should not list this remote video', async function () {
127 const { total, data } = await servers[0].videos.list()
128 expect(total).to.equal(1)
129 expect(data).to.have.lengthOf(1)
130 expect(data[0].name).to.equal('video 1 on server 1')
131 })
132
133 it('Should update video of server 2, and refresh it on server 1', async function () {
134 this.timeout(120000)
135
136 const channelAttributes = {
137 name: 'super_channel',
138 displayName: 'super channel'
139 }
140 const created = await servers[1].channels.create({ attributes: channelAttributes })
141 const videoChannelId = created.id
142
143 const attributes = {
144 name: 'updated',
145 tag: [ 'tag1', 'tag2' ],
146 privacy: VideoPrivacy.UNLISTED,
147 channelId: videoChannelId
148 }
149 await servers[1].videos.update({ id: videoServer2UUID, attributes })
150
151 await waitJobs(servers)
152 // Expire video
153 await wait(10000)
154
155 // Will run refresh async
156 const search = servers[1].url + '/videos/watch/' + videoServer2UUID
157 await command.searchVideos({ search, token: servers[0].accessToken })
158
159 // Wait refresh
160 await wait(5000)
161
162 const body = await command.searchVideos({ search, token: servers[0].accessToken })
163 expect(body.total).to.equal(1)
164 expect(body.data).to.have.lengthOf(1)
165
166 const video = body.data[0]
167 expect(video.name).to.equal('updated')
168 expect(video.channel.name).to.equal('super_channel')
169 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED)
170 })
171
172 it('Should delete video of server 2, and delete it on server 1', async function () {
173 this.timeout(120000)
174
175 await servers[1].videos.remove({ id: videoServer2UUID })
176
177 await waitJobs(servers)
178 // Expire video
179 await wait(10000)
180
181 // Will run refresh async
182 const search = servers[1].url + '/videos/watch/' + videoServer2UUID
183 await command.searchVideos({ search, token: servers[0].accessToken })
184
185 // Wait refresh
186 await wait(5000)
187
188 const body = await command.searchVideos({ search, token: servers[0].accessToken })
189 expect(body.total).to.equal(0)
190 expect(body.data).to.have.lengthOf(0)
191 })
192
193 after(async function () {
194 await cleanupTests(servers)
195 })
196})
diff --git a/packages/tests/src/api/search/search-channels.ts b/packages/tests/src/api/search/search-channels.ts
new file mode 100644
index 000000000..36596e036
--- /dev/null
+++ b/packages/tests/src/api/search/search-channels.ts
@@ -0,0 +1,159 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { VideoChannel } from '@peertube/peertube-models'
5import {
6 cleanupTests,
7 createSingleServer,
8 doubleFollow,
9 PeerTubeServer,
10 SearchCommand,
11 setAccessTokensToServers,
12 setDefaultAccountAvatar,
13 setDefaultChannelAvatar
14} from '@peertube/peertube-server-commands'
15
16describe('Test channels search', function () {
17 let server: PeerTubeServer
18 let remoteServer: PeerTubeServer
19 let command: SearchCommand
20
21 before(async function () {
22 this.timeout(120000)
23
24 const servers = await Promise.all([
25 createSingleServer(1),
26 createSingleServer(2)
27 ])
28 server = servers[0]
29 remoteServer = servers[1]
30
31 await setAccessTokensToServers([ server, remoteServer ])
32 await setDefaultChannelAvatar(server)
33 await setDefaultAccountAvatar(server)
34
35 await servers[1].config.disableTranscoding()
36
37 {
38 await server.users.create({ username: 'user1' })
39 const channel = {
40 name: 'squall_channel',
41 displayName: 'Squall channel'
42 }
43 await server.channels.create({ attributes: channel })
44 }
45
46 {
47 await remoteServer.users.create({ username: 'user1' })
48 const channel = {
49 name: 'zell_channel',
50 displayName: 'Zell channel'
51 }
52 const { id } = await remoteServer.channels.create({ attributes: channel })
53
54 await remoteServer.videos.upload({ attributes: { channelId: id } })
55 }
56
57 await doubleFollow(server, remoteServer)
58
59 command = server.search
60 })
61
62 it('Should make a simple search and not have results', async function () {
63 const body = await command.searchChannels({ search: 'abc' })
64
65 expect(body.total).to.equal(0)
66 expect(body.data).to.have.lengthOf(0)
67 })
68
69 it('Should make a search and have results', async function () {
70 {
71 const search = {
72 search: 'Squall',
73 start: 0,
74 count: 1
75 }
76 const body = await command.advancedChannelSearch({ search })
77 expect(body.total).to.equal(1)
78 expect(body.data).to.have.lengthOf(1)
79
80 const channel: VideoChannel = body.data[0]
81 expect(channel.name).to.equal('squall_channel')
82 expect(channel.displayName).to.equal('Squall channel')
83 }
84
85 {
86 const search = {
87 search: 'Squall',
88 start: 1,
89 count: 1
90 }
91
92 const body = await command.advancedChannelSearch({ search })
93 expect(body.total).to.equal(1)
94 expect(body.data).to.have.lengthOf(0)
95 }
96 })
97
98 it('Should filter by host', async function () {
99 {
100 const search = { search: 'channel', host: remoteServer.host }
101
102 const body = await command.advancedChannelSearch({ search })
103 expect(body.total).to.equal(1)
104 expect(body.data).to.have.lengthOf(1)
105 expect(body.data[0].displayName).to.equal('Zell channel')
106 }
107
108 {
109 const search = { search: 'Sq', host: server.host }
110
111 const body = await command.advancedChannelSearch({ search })
112 expect(body.total).to.equal(1)
113 expect(body.data).to.have.lengthOf(1)
114 expect(body.data[0].displayName).to.equal('Squall channel')
115 }
116
117 {
118 const search = { search: 'Squall', host: 'example.com' }
119
120 const body = await command.advancedChannelSearch({ search })
121 expect(body.total).to.equal(0)
122 expect(body.data).to.have.lengthOf(0)
123 }
124 })
125
126 it('Should filter by names', async function () {
127 {
128 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel', 'zell_channel' ] } })
129 expect(body.total).to.equal(1)
130 expect(body.data).to.have.lengthOf(1)
131 expect(body.data[0].displayName).to.equal('Squall channel')
132 }
133
134 {
135 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel@' + server.host ] } })
136 expect(body.total).to.equal(1)
137 expect(body.data).to.have.lengthOf(1)
138 expect(body.data[0].displayName).to.equal('Squall channel')
139 }
140
141 {
142 const body = await command.advancedChannelSearch({ search: { handles: [ 'chocobozzz_channel' ] } })
143 expect(body.total).to.equal(0)
144 expect(body.data).to.have.lengthOf(0)
145 }
146
147 {
148 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel', 'zell_channel@' + remoteServer.host ] } })
149 expect(body.total).to.equal(2)
150 expect(body.data).to.have.lengthOf(2)
151 expect(body.data[0].displayName).to.equal('Squall channel')
152 expect(body.data[1].displayName).to.equal('Zell channel')
153 }
154 })
155
156 after(async function () {
157 await cleanupTests([ server, remoteServer ])
158 })
159})
diff --git a/packages/tests/src/api/search/search-index.ts b/packages/tests/src/api/search/search-index.ts
new file mode 100644
index 000000000..4bac7ea94
--- /dev/null
+++ b/packages/tests/src/api/search/search-index.ts
@@ -0,0 +1,438 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import {
5 BooleanBothQuery,
6 VideoChannelsSearchQuery,
7 VideoPlaylistPrivacy,
8 VideoPlaylistsSearchQuery,
9 VideoPlaylistType,
10 VideosSearchQuery
11} from '@peertube/peertube-models'
12import {
13 cleanupTests,
14 createSingleServer,
15 PeerTubeServer,
16 SearchCommand,
17 setAccessTokensToServers
18} from '@peertube/peertube-server-commands'
19
20describe('Test index search', function () {
21 const localVideoName = 'local video' + new Date().toISOString()
22
23 let server: PeerTubeServer = null
24 let command: SearchCommand
25
26 before(async function () {
27 this.timeout(30000)
28
29 server = await createSingleServer(1)
30
31 await setAccessTokensToServers([ server ])
32
33 await server.videos.upload({ attributes: { name: localVideoName } })
34
35 command = server.search
36 })
37
38 describe('Default search', async function () {
39
40 it('Should make a local videos search by default', async function () {
41 await server.config.updateCustomSubConfig({
42 newConfig: {
43 search: {
44 searchIndex: {
45 enabled: true,
46 isDefaultSearch: false,
47 disableLocalSearch: false
48 }
49 }
50 }
51 })
52
53 const body = await command.searchVideos({ search: 'local video' })
54
55 expect(body.total).to.equal(1)
56 expect(body.data[0].name).to.equal(localVideoName)
57 })
58
59 it('Should make a local channels search by default', async function () {
60 const body = await command.searchChannels({ search: 'root' })
61
62 expect(body.total).to.equal(1)
63 expect(body.data[0].name).to.equal('root_channel')
64 expect(body.data[0].host).to.equal(server.host)
65 })
66
67 it('Should make an index videos search by default', async function () {
68 await server.config.updateCustomSubConfig({
69 newConfig: {
70 search: {
71 searchIndex: {
72 enabled: true,
73 isDefaultSearch: true,
74 disableLocalSearch: false
75 }
76 }
77 }
78 })
79
80 const body = await command.searchVideos({ search: 'local video' })
81 expect(body.total).to.be.greaterThan(2)
82 })
83
84 it('Should make an index channels search by default', async function () {
85 const body = await command.searchChannels({ search: 'root' })
86 expect(body.total).to.be.greaterThan(2)
87 })
88 })
89
90 describe('Videos search', async function () {
91
92 async function check (search: VideosSearchQuery, exists = true) {
93 const body = await command.advancedVideoSearch({ search })
94
95 if (exists === false) {
96 expect(body.total).to.equal(0)
97 expect(body.data).to.have.lengthOf(0)
98 return
99 }
100
101 expect(body.total).to.equal(1)
102 expect(body.data).to.have.lengthOf(1)
103
104 const video = body.data[0]
105
106 expect(video.name).to.equal('What is PeerTube?')
107 expect(video.category.label).to.equal('Science & Technology')
108 expect(video.licence.label).to.equal('Attribution - Share Alike')
109 expect(video.privacy.label).to.equal('Public')
110 expect(video.duration).to.equal(113)
111 expect(video.thumbnailUrl.startsWith('https://framatube.org/static/thumbnails')).to.be.true
112
113 expect(video.account.host).to.equal('framatube.org')
114 expect(video.account.name).to.equal('framasoft')
115 expect(video.account.url).to.equal('https://framatube.org/accounts/framasoft')
116 expect(video.account.avatars.length).to.equal(2, 'Account should have one avatar image')
117
118 expect(video.channel.host).to.equal('framatube.org')
119 expect(video.channel.name).to.equal('joinpeertube')
120 expect(video.channel.url).to.equal('https://framatube.org/video-channels/joinpeertube')
121 expect(video.channel.avatars.length).to.equal(2, 'Channel should have one avatar image')
122 }
123
124 const baseSearch: VideosSearchQuery = {
125 search: 'what is peertube',
126 start: 0,
127 count: 2,
128 categoryOneOf: [ 15 ],
129 licenceOneOf: [ 2 ],
130 tagsAllOf: [ 'framasoft', 'peertube' ],
131 startDate: '2018-10-01T10:50:46.396Z',
132 endDate: '2018-10-01T10:55:46.396Z'
133 }
134
135 it('Should make a simple search and not have results', async function () {
136 const body = await command.searchVideos({ search: 'djidane'.repeat(50) })
137
138 expect(body.total).to.equal(0)
139 expect(body.data).to.have.lengthOf(0)
140 })
141
142 it('Should make a simple search and have results', async function () {
143 const body = await command.searchVideos({ search: 'What is PeerTube' })
144
145 expect(body.total).to.be.greaterThan(1)
146 })
147
148 it('Should make a simple search', async function () {
149 await check(baseSearch)
150 })
151
152 it('Should search by start date', async function () {
153 const search = { ...baseSearch, startDate: '2018-10-01T10:54:46.396Z' }
154 await check(search, false)
155 })
156
157 it('Should search by tags', async function () {
158 const search = { ...baseSearch, tagsAllOf: [ 'toto', 'framasoft' ] }
159 await check(search, false)
160 })
161
162 it('Should search by duration', async function () {
163 const search = { ...baseSearch, durationMin: 2000 }
164 await check(search, false)
165 })
166
167 it('Should search by nsfw attribute', async function () {
168 {
169 const search = { ...baseSearch, nsfw: 'true' as BooleanBothQuery }
170 await check(search, false)
171 }
172
173 {
174 const search = { ...baseSearch, nsfw: 'false' as BooleanBothQuery }
175 await check(search, true)
176 }
177
178 {
179 const search = { ...baseSearch, nsfw: 'both' as BooleanBothQuery }
180 await check(search, true)
181 }
182 })
183
184 it('Should search by host', async function () {
185 {
186 const search = { ...baseSearch, host: 'example.com' }
187 await check(search, false)
188 }
189
190 {
191 const search = { ...baseSearch, host: 'framatube.org' }
192 await check(search, true)
193 }
194 })
195
196 it('Should search by uuids', async function () {
197 const goodUUID = '9c9de5e8-0a1e-484a-b099-e80766180a6d'
198 const goodShortUUID = 'kkGMgK9ZtnKfYAgnEtQxbv'
199 const badUUID = 'c29c5b77-4a04-493d-96a9-2e9267e308f0'
200 const badShortUUID = 'rP5RgUeX9XwTSrspCdkDej'
201
202 {
203 const uuidsMatrix = [
204 [ goodUUID ],
205 [ goodUUID, badShortUUID ],
206 [ badShortUUID, goodShortUUID ],
207 [ goodUUID, goodShortUUID ]
208 ]
209
210 for (const uuids of uuidsMatrix) {
211 const search = { ...baseSearch, uuids }
212 await check(search, true)
213 }
214 }
215
216 {
217 const uuidsMatrix = [
218 [ badUUID ],
219 [ badShortUUID ]
220 ]
221
222 for (const uuids of uuidsMatrix) {
223 const search = { ...baseSearch, uuids }
224 await check(search, false)
225 }
226 }
227 })
228
229 it('Should have a correct pagination', async function () {
230 const search = {
231 search: 'video',
232 start: 0,
233 count: 5
234 }
235
236 const body = await command.advancedVideoSearch({ search })
237
238 expect(body.total).to.be.greaterThan(5)
239 expect(body.data).to.have.lengthOf(5)
240 })
241
242 it('Should use the nsfw instance policy as default', async function () {
243 let nsfwUUID: string
244
245 {
246 await server.config.updateCustomSubConfig({
247 newConfig: {
248 instance: { defaultNSFWPolicy: 'display' }
249 }
250 })
251
252 const body = await command.searchVideos({ search: 'NSFW search index', sort: '-match' })
253 expect(body.data).to.have.length.greaterThan(0)
254
255 const video = body.data[0]
256 expect(video.nsfw).to.be.true
257
258 nsfwUUID = video.uuid
259 }
260
261 {
262 await server.config.updateCustomSubConfig({
263 newConfig: {
264 instance: { defaultNSFWPolicy: 'do_not_list' }
265 }
266 })
267
268 const body = await command.searchVideos({ search: 'NSFW search index', sort: '-match' })
269
270 try {
271 expect(body.data).to.have.lengthOf(0)
272 } catch {
273 const video = body.data[0]
274
275 expect(video.uuid).not.equal(nsfwUUID)
276 }
277 }
278 })
279 })
280
281 describe('Channels search', async function () {
282
283 async function check (search: VideoChannelsSearchQuery, exists = true) {
284 const body = await command.advancedChannelSearch({ search })
285
286 if (exists === false) {
287 expect(body.total).to.equal(0)
288 expect(body.data).to.have.lengthOf(0)
289 return
290 }
291
292 expect(body.total).to.be.greaterThan(0)
293 expect(body.data).to.have.length.greaterThan(0)
294
295 const videoChannel = body.data[0]
296 expect(videoChannel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8')
297 expect(videoChannel.host).to.equal('framatube.org')
298 expect(videoChannel.avatars.length).to.equal(2, 'Channel should have two avatar images')
299 expect(videoChannel.displayName).to.exist
300
301 expect(videoChannel.ownerAccount.url).to.equal('https://framatube.org/accounts/framasoft')
302 expect(videoChannel.ownerAccount.name).to.equal('framasoft')
303 expect(videoChannel.ownerAccount.host).to.equal('framatube.org')
304 expect(videoChannel.ownerAccount.avatars.length).to.equal(2, 'Account should have two avatar images')
305 }
306
307 it('Should make a simple search and not have results', async function () {
308 const body = await command.searchChannels({ search: 'a'.repeat(500) })
309
310 expect(body.total).to.equal(0)
311 expect(body.data).to.have.lengthOf(0)
312 })
313
314 it('Should make a search and have results', async function () {
315 await check({ search: 'Framasoft', sort: 'createdAt' }, true)
316 })
317
318 it('Should make host search and have appropriate results', async function () {
319 await check({ search: 'Framasoft videos', host: 'example.com' }, false)
320 await check({ search: 'Framasoft videos', host: 'framatube.org' }, true)
321 })
322
323 it('Should make handles search and have appropriate results', async function () {
324 await check({ handles: [ 'bf54d359-cfad-4935-9d45-9d6be93f63e8@framatube.org' ] }, true)
325 await check({ handles: [ 'jeanine', 'bf54d359-cfad-4935-9d45-9d6be93f63e8@framatube.org' ] }, true)
326 await check({ handles: [ 'jeanine', 'chocobozzz_channel2@peertube2.cpy.re' ] }, false)
327 })
328
329 it('Should have a correct pagination', async function () {
330 const body = await command.advancedChannelSearch({ search: { search: 'root', start: 0, count: 2 } })
331
332 expect(body.total).to.be.greaterThan(2)
333 expect(body.data).to.have.lengthOf(2)
334 })
335 })
336
337 describe('Playlists search', async function () {
338
339 async function check (search: VideoPlaylistsSearchQuery, exists = true) {
340 const body = await command.advancedPlaylistSearch({ search })
341
342 if (exists === false) {
343 expect(body.total).to.equal(0)
344 expect(body.data).to.have.lengthOf(0)
345 return
346 }
347
348 expect(body.total).to.be.greaterThan(0)
349 expect(body.data).to.have.length.greaterThan(0)
350
351 const videoPlaylist = body.data[0]
352
353 expect(videoPlaylist.url).to.equal('https://peertube2.cpy.re/videos/watch/playlist/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a')
354 expect(videoPlaylist.thumbnailUrl).to.exist
355 expect(videoPlaylist.embedUrl).to.equal('https://peertube2.cpy.re/video-playlists/embed/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a')
356
357 expect(videoPlaylist.type.id).to.equal(VideoPlaylistType.REGULAR)
358 expect(videoPlaylist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
359 expect(videoPlaylist.videosLength).to.exist
360
361 expect(videoPlaylist.createdAt).to.exist
362 expect(videoPlaylist.updatedAt).to.exist
363
364 expect(videoPlaylist.uuid).to.equal('73804a40-da9a-40c2-b1eb-2c6d9eec8f0a')
365 expect(videoPlaylist.displayName).to.exist
366
367 expect(videoPlaylist.ownerAccount.url).to.equal('https://peertube2.cpy.re/accounts/chocobozzz')
368 expect(videoPlaylist.ownerAccount.name).to.equal('chocobozzz')
369 expect(videoPlaylist.ownerAccount.host).to.equal('peertube2.cpy.re')
370 expect(videoPlaylist.ownerAccount.avatars.length).to.equal(2, 'Account should have two avatar images')
371
372 expect(videoPlaylist.videoChannel.url).to.equal('https://peertube2.cpy.re/video-channels/chocobozzz_channel')
373 expect(videoPlaylist.videoChannel.name).to.equal('chocobozzz_channel')
374 expect(videoPlaylist.videoChannel.host).to.equal('peertube2.cpy.re')
375 expect(videoPlaylist.videoChannel.avatars.length).to.equal(2, 'Channel should have two avatar images')
376 }
377
378 it('Should make a simple search and not have results', async function () {
379 const body = await command.searchPlaylists({ search: 'a'.repeat(500) })
380
381 expect(body.total).to.equal(0)
382 expect(body.data).to.have.lengthOf(0)
383 })
384
385 it('Should make a search and have results', async function () {
386 await check({ search: 'E2E playlist', sort: '-match' }, true)
387 })
388
389 it('Should make host search and have appropriate results', async function () {
390 await check({ search: 'E2E playlist', host: 'example.com' }, false)
391 await check({ search: 'E2E playlist', host: 'peertube2.cpy.re', sort: '-match' }, true)
392 })
393
394 it('Should make a search by uuids and have appropriate results', async function () {
395 const goodUUID = '73804a40-da9a-40c2-b1eb-2c6d9eec8f0a'
396 const goodShortUUID = 'fgei1ws1oa6FCaJ2qZPG29'
397 const badUUID = 'c29c5b77-4a04-493d-96a9-2e9267e308f0'
398 const badShortUUID = 'rP5RgUeX9XwTSrspCdkDej'
399
400 {
401 const uuidsMatrix = [
402 [ goodUUID ],
403 [ goodUUID, badShortUUID ],
404 [ badShortUUID, goodShortUUID ],
405 [ goodUUID, goodShortUUID ]
406 ]
407
408 for (const uuids of uuidsMatrix) {
409 const search = { search: 'E2E playlist', sort: '-match', uuids }
410 await check(search, true)
411 }
412 }
413
414 {
415 const uuidsMatrix = [
416 [ badUUID ],
417 [ badShortUUID ]
418 ]
419
420 for (const uuids of uuidsMatrix) {
421 const search = { search: 'E2E playlist', sort: '-match', uuids }
422 await check(search, false)
423 }
424 }
425 })
426
427 it('Should have a correct pagination', async function () {
428 const body = await command.advancedChannelSearch({ search: { search: 'root', start: 0, count: 2 } })
429
430 expect(body.total).to.be.greaterThan(2)
431 expect(body.data).to.have.lengthOf(2)
432 })
433 })
434
435 after(async function () {
436 await cleanupTests([ server ])
437 })
438})
diff --git a/packages/tests/src/api/search/search-playlists.ts b/packages/tests/src/api/search/search-playlists.ts
new file mode 100644
index 000000000..cd16e202e
--- /dev/null
+++ b/packages/tests/src/api/search/search-playlists.ts
@@ -0,0 +1,180 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { VideoPlaylistPrivacy } from '@peertube/peertube-models'
5import {
6 cleanupTests,
7 createSingleServer,
8 doubleFollow,
9 PeerTubeServer,
10 SearchCommand,
11 setAccessTokensToServers,
12 setDefaultAccountAvatar,
13 setDefaultChannelAvatar,
14 setDefaultVideoChannel
15} from '@peertube/peertube-server-commands'
16
17describe('Test playlists search', function () {
18 let server: PeerTubeServer
19 let remoteServer: PeerTubeServer
20 let command: SearchCommand
21 let playlistUUID: string
22 let playlistShortUUID: string
23
24 before(async function () {
25 this.timeout(120000)
26
27 const servers = await Promise.all([
28 createSingleServer(1),
29 createSingleServer(2)
30 ])
31 server = servers[0]
32 remoteServer = servers[1]
33
34 await setAccessTokensToServers([ remoteServer, server ])
35 await setDefaultVideoChannel([ remoteServer, server ])
36 await setDefaultChannelAvatar([ remoteServer, server ])
37 await setDefaultAccountAvatar([ remoteServer, server ])
38
39 await servers[1].config.disableTranscoding()
40
41 {
42 const videoId = (await server.videos.upload()).uuid
43
44 const attributes = {
45 displayName: 'Dr. Kenzo Tenma hospital videos',
46 privacy: VideoPlaylistPrivacy.PUBLIC,
47 videoChannelId: server.store.channel.id
48 }
49 const created = await server.playlists.create({ attributes })
50 playlistUUID = created.uuid
51 playlistShortUUID = created.shortUUID
52
53 await server.playlists.addElement({ playlistId: created.id, attributes: { videoId } })
54 }
55
56 {
57 const videoId = (await remoteServer.videos.upload()).uuid
58
59 const attributes = {
60 displayName: 'Johan & Anna Libert music videos',
61 privacy: VideoPlaylistPrivacy.PUBLIC,
62 videoChannelId: remoteServer.store.channel.id
63 }
64 const created = await remoteServer.playlists.create({ attributes })
65
66 await remoteServer.playlists.addElement({ playlistId: created.id, attributes: { videoId } })
67 }
68
69 {
70 const attributes = {
71 displayName: 'Inspector Lunge playlist',
72 privacy: VideoPlaylistPrivacy.PUBLIC,
73 videoChannelId: server.store.channel.id
74 }
75 await server.playlists.create({ attributes })
76 }
77
78 await doubleFollow(server, remoteServer)
79
80 command = server.search
81 })
82
83 it('Should make a simple search and not have results', async function () {
84 const body = await command.searchPlaylists({ search: 'abc' })
85
86 expect(body.total).to.equal(0)
87 expect(body.data).to.have.lengthOf(0)
88 })
89
90 it('Should make a search and have results', async function () {
91 {
92 const search = {
93 search: 'tenma',
94 start: 0,
95 count: 1
96 }
97 const body = await command.advancedPlaylistSearch({ search })
98 expect(body.total).to.equal(1)
99 expect(body.data).to.have.lengthOf(1)
100
101 const playlist = body.data[0]
102 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos')
103 expect(playlist.url).to.equal(server.url + '/video-playlists/' + playlist.uuid)
104 }
105
106 {
107 const search = {
108 search: 'Anna Livert music',
109 start: 0,
110 count: 1
111 }
112 const body = await command.advancedPlaylistSearch({ search })
113 expect(body.total).to.equal(1)
114 expect(body.data).to.have.lengthOf(1)
115
116 const playlist = body.data[0]
117 expect(playlist.displayName).to.equal('Johan & Anna Libert music videos')
118 }
119 })
120
121 it('Should filter by host', async function () {
122 {
123 const search = { search: 'tenma', host: server.host }
124 const body = await command.advancedPlaylistSearch({ search })
125 expect(body.total).to.equal(1)
126 expect(body.data).to.have.lengthOf(1)
127
128 const playlist = body.data[0]
129 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos')
130 }
131
132 {
133 const search = { search: 'Anna', host: 'example.com' }
134 const body = await command.advancedPlaylistSearch({ search })
135 expect(body.total).to.equal(0)
136 expect(body.data).to.have.lengthOf(0)
137 }
138
139 {
140 const search = { search: 'video', host: remoteServer.host }
141 const body = await command.advancedPlaylistSearch({ search })
142 expect(body.total).to.equal(1)
143 expect(body.data).to.have.lengthOf(1)
144
145 const playlist = body.data[0]
146 expect(playlist.displayName).to.equal('Johan & Anna Libert music videos')
147 }
148 })
149
150 it('Should filter by UUIDs', async function () {
151 for (const uuid of [ playlistUUID, playlistShortUUID ]) {
152 const body = await command.advancedPlaylistSearch({ search: { uuids: [ uuid ] } })
153
154 expect(body.total).to.equal(1)
155 expect(body.data[0].displayName).to.equal('Dr. Kenzo Tenma hospital videos')
156 }
157
158 {
159 const body = await command.advancedPlaylistSearch({ search: { uuids: [ 'dfd70b83-639f-4980-94af-304a56ab4b35' ] } })
160
161 expect(body.total).to.equal(0)
162 expect(body.data).to.have.lengthOf(0)
163 }
164 })
165
166 it('Should not display playlists without videos', async function () {
167 const search = {
168 search: 'Lunge',
169 start: 0,
170 count: 1
171 }
172 const body = await command.advancedPlaylistSearch({ search })
173 expect(body.total).to.equal(0)
174 expect(body.data).to.have.lengthOf(0)
175 })
176
177 after(async function () {
178 await cleanupTests([ server, remoteServer ])
179 })
180})
diff --git a/packages/tests/src/api/search/search-videos.ts b/packages/tests/src/api/search/search-videos.ts
new file mode 100644
index 000000000..0739f0886
--- /dev/null
+++ b/packages/tests/src/api/search/search-videos.ts
@@ -0,0 +1,568 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { wait } from '@peertube/peertube-core-utils'
5import { VideoPrivacy } from '@peertube/peertube-models'
6import {
7 cleanupTests,
8 createSingleServer,
9 doubleFollow,
10 PeerTubeServer,
11 SearchCommand,
12 setAccessTokensToServers,
13 setDefaultAccountAvatar,
14 setDefaultChannelAvatar,
15 setDefaultVideoChannel,
16 stopFfmpeg
17} from '@peertube/peertube-server-commands'
18
19describe('Test videos search', function () {
20 let server: PeerTubeServer
21 let remoteServer: PeerTubeServer
22 let startDate: string
23 let videoUUID: string
24 let videoShortUUID: string
25
26 let command: SearchCommand
27
28 before(async function () {
29 this.timeout(360000)
30
31 const servers = await Promise.all([
32 createSingleServer(1),
33 createSingleServer(2)
34 ])
35 server = servers[0]
36 remoteServer = servers[1]
37
38 await setAccessTokensToServers([ server, remoteServer ])
39 await setDefaultVideoChannel([ server, remoteServer ])
40 await setDefaultChannelAvatar(server)
41 await setDefaultAccountAvatar(servers)
42
43 {
44 const attributes1 = {
45 name: '1111 2222 3333',
46 fixture: '60fps_720p_small.mp4', // 2 seconds
47 category: 1,
48 licence: 1,
49 nsfw: false,
50 language: 'fr'
51 }
52 await server.videos.upload({ attributes: attributes1 })
53
54 const attributes2 = { ...attributes1, name: attributes1.name + ' - 2', fixture: 'video_short.mp4' }
55 await server.videos.upload({ attributes: attributes2 })
56
57 {
58 const attributes3 = { ...attributes1, name: attributes1.name + ' - 3', language: undefined }
59 const { id, uuid, shortUUID } = await server.videos.upload({ attributes: attributes3 })
60 videoUUID = uuid
61 videoShortUUID = shortUUID
62
63 await server.captions.add({
64 language: 'en',
65 videoId: id,
66 fixture: 'subtitle-good2.vtt',
67 mimeType: 'application/octet-stream'
68 })
69
70 await server.captions.add({
71 language: 'aa',
72 videoId: id,
73 fixture: 'subtitle-good2.vtt',
74 mimeType: 'application/octet-stream'
75 })
76 }
77
78 const attributes4 = { ...attributes1, name: attributes1.name + ' - 4', language: 'pl', nsfw: true }
79 await server.videos.upload({ attributes: attributes4 })
80
81 await wait(1000)
82
83 startDate = new Date().toISOString()
84
85 const attributes5 = { ...attributes1, name: attributes1.name + ' - 5', licence: 2, language: undefined }
86 await server.videos.upload({ attributes: attributes5 })
87
88 const attributes6 = { ...attributes1, name: attributes1.name + ' - 6', tags: [ 't1', 't2' ] }
89 await server.videos.upload({ attributes: attributes6 })
90
91 const attributes7 = { ...attributes1, name: attributes1.name + ' - 7', originallyPublishedAt: '2019-02-12T09:58:08.286Z' }
92 await server.videos.upload({ attributes: attributes7 })
93
94 const attributes8 = { ...attributes1, name: attributes1.name + ' - 8', licence: 4 }
95 await server.videos.upload({ attributes: attributes8 })
96 }
97
98 {
99 const attributes = {
100 name: '3333 4444 5555',
101 fixture: 'video_short.mp4',
102 category: 2,
103 licence: 2,
104 language: 'en'
105 }
106 await server.videos.upload({ attributes })
107
108 await server.videos.upload({ attributes: { ...attributes, name: attributes.name + ' duplicate' } })
109 }
110
111 {
112 const attributes = {
113 name: '6666 7777 8888',
114 fixture: 'video_short.mp4',
115 category: 3,
116 licence: 3,
117 language: 'pl'
118 }
119 await server.videos.upload({ attributes })
120 }
121
122 {
123 const attributes1 = {
124 name: '9999',
125 tags: [ 'aaaa', 'bbbb', 'cccc' ],
126 category: 1
127 }
128 await server.videos.upload({ attributes: attributes1 })
129 await server.videos.upload({ attributes: { ...attributes1, category: 2 } })
130
131 await server.videos.upload({ attributes: { ...attributes1, tags: [ 'cccc', 'dddd' ] } })
132 await server.videos.upload({ attributes: { ...attributes1, tags: [ 'eeee', 'ffff' ] } })
133 }
134
135 {
136 const attributes1 = {
137 name: 'aaaa 2',
138 category: 1
139 }
140 await server.videos.upload({ attributes: attributes1 })
141 await server.videos.upload({ attributes: { ...attributes1, category: 2 } })
142 }
143
144 {
145 await remoteServer.videos.upload({ attributes: { name: 'remote video 1' } })
146 await remoteServer.videos.upload({ attributes: { name: 'remote video 2' } })
147 }
148
149 await doubleFollow(server, remoteServer)
150
151 command = server.search
152 })
153
154 it('Should make a simple search and not have results', async function () {
155 const body = await command.searchVideos({ search: 'abc' })
156
157 expect(body.total).to.equal(0)
158 expect(body.data).to.have.lengthOf(0)
159 })
160
161 it('Should make a simple search and have results', async function () {
162 const body = await command.searchVideos({ search: '4444 5555 duplicate' })
163
164 expect(body.total).to.equal(2)
165
166 const videos = body.data
167 expect(videos).to.have.lengthOf(2)
168
169 // bestmatch
170 expect(videos[0].name).to.equal('3333 4444 5555 duplicate')
171 expect(videos[1].name).to.equal('3333 4444 5555')
172 })
173
174 it('Should make a search on tags too, and have results', async function () {
175 const search = {
176 search: 'aaaa',
177 categoryOneOf: [ 1 ]
178 }
179 const body = await command.advancedVideoSearch({ search })
180
181 expect(body.total).to.equal(2)
182
183 const videos = body.data
184 expect(videos).to.have.lengthOf(2)
185
186 // bestmatch
187 expect(videos[0].name).to.equal('aaaa 2')
188 expect(videos[1].name).to.equal('9999')
189 })
190
191 it('Should filter on tags without a search', async function () {
192 const search = {
193 tagsAllOf: [ 'bbbb' ]
194 }
195 const body = await command.advancedVideoSearch({ search })
196
197 expect(body.total).to.equal(2)
198
199 const videos = body.data
200 expect(videos).to.have.lengthOf(2)
201
202 expect(videos[0].name).to.equal('9999')
203 expect(videos[1].name).to.equal('9999')
204 })
205
206 it('Should filter on category without a search', async function () {
207 const search = {
208 categoryOneOf: [ 3 ]
209 }
210 const body = await command.advancedVideoSearch({ search })
211
212 expect(body.total).to.equal(1)
213
214 const videos = body.data
215 expect(videos).to.have.lengthOf(1)
216
217 expect(videos[0].name).to.equal('6666 7777 8888')
218 })
219
220 it('Should search by tags (one of)', async function () {
221 const query = {
222 search: '9999',
223 categoryOneOf: [ 1 ],
224 tagsOneOf: [ 'aAaa', 'ffff' ]
225 }
226
227 {
228 const body = await command.advancedVideoSearch({ search: query })
229 expect(body.total).to.equal(2)
230 }
231
232 {
233 const body = await command.advancedVideoSearch({ search: { ...query, tagsOneOf: [ 'blabla' ] } })
234 expect(body.total).to.equal(0)
235 }
236 })
237
238 it('Should search by tags (all of)', async function () {
239 const query = {
240 search: '9999',
241 categoryOneOf: [ 1 ],
242 tagsAllOf: [ 'CCcc' ]
243 }
244
245 {
246 const body = await command.advancedVideoSearch({ search: query })
247 expect(body.total).to.equal(2)
248 }
249
250 {
251 const body = await command.advancedVideoSearch({ search: { ...query, tagsAllOf: [ 'blAbla' ] } })
252 expect(body.total).to.equal(0)
253 }
254
255 {
256 const body = await command.advancedVideoSearch({ search: { ...query, tagsAllOf: [ 'bbbb', 'CCCC' ] } })
257 expect(body.total).to.equal(1)
258 }
259 })
260
261 it('Should search by category', async function () {
262 const query = {
263 search: '6666',
264 categoryOneOf: [ 3 ]
265 }
266
267 {
268 const body = await command.advancedVideoSearch({ search: query })
269 expect(body.total).to.equal(1)
270 expect(body.data[0].name).to.equal('6666 7777 8888')
271 }
272
273 {
274 const body = await command.advancedVideoSearch({ search: { ...query, categoryOneOf: [ 2 ] } })
275 expect(body.total).to.equal(0)
276 }
277 })
278
279 it('Should search by licence', async function () {
280 const query = {
281 search: '4444 5555',
282 licenceOneOf: [ 2 ]
283 }
284
285 {
286 const body = await command.advancedVideoSearch({ search: query })
287 expect(body.total).to.equal(2)
288 expect(body.data[0].name).to.equal('3333 4444 5555')
289 expect(body.data[1].name).to.equal('3333 4444 5555 duplicate')
290 }
291
292 {
293 const body = await command.advancedVideoSearch({ search: { ...query, licenceOneOf: [ 3 ] } })
294 expect(body.total).to.equal(0)
295 }
296 })
297
298 it('Should search by languages', async function () {
299 const query = {
300 search: '1111 2222 3333',
301 languageOneOf: [ 'pl', 'en' ]
302 }
303
304 {
305 const body = await command.advancedVideoSearch({ search: query })
306 expect(body.total).to.equal(2)
307 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
308 expect(body.data[1].name).to.equal('1111 2222 3333 - 4')
309 }
310
311 {
312 const body = await command.advancedVideoSearch({ search: { ...query, languageOneOf: [ 'pl', 'en', '_unknown' ] } })
313 expect(body.total).to.equal(3)
314 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
315 expect(body.data[1].name).to.equal('1111 2222 3333 - 4')
316 expect(body.data[2].name).to.equal('1111 2222 3333 - 5')
317 }
318
319 {
320 const body = await command.advancedVideoSearch({ search: { ...query, languageOneOf: [ 'eo' ] } })
321 expect(body.total).to.equal(0)
322 }
323 })
324
325 it('Should search by start date', async function () {
326 const query = {
327 search: '1111 2222 3333',
328 startDate
329 }
330
331 const body = await command.advancedVideoSearch({ search: query })
332 expect(body.total).to.equal(4)
333
334 const videos = body.data
335 expect(videos[0].name).to.equal('1111 2222 3333 - 5')
336 expect(videos[1].name).to.equal('1111 2222 3333 - 6')
337 expect(videos[2].name).to.equal('1111 2222 3333 - 7')
338 expect(videos[3].name).to.equal('1111 2222 3333 - 8')
339 })
340
341 it('Should make an advanced search', async function () {
342 const query = {
343 search: '1111 2222 3333',
344 languageOneOf: [ 'pl', 'fr' ],
345 durationMax: 4,
346 nsfw: 'false' as 'false',
347 licenceOneOf: [ 1, 4 ]
348 }
349
350 const body = await command.advancedVideoSearch({ search: query })
351 expect(body.total).to.equal(4)
352
353 const videos = body.data
354 expect(videos[0].name).to.equal('1111 2222 3333')
355 expect(videos[1].name).to.equal('1111 2222 3333 - 6')
356 expect(videos[2].name).to.equal('1111 2222 3333 - 7')
357 expect(videos[3].name).to.equal('1111 2222 3333 - 8')
358 })
359
360 it('Should make an advanced search and sort results', async function () {
361 const query = {
362 search: '1111 2222 3333',
363 languageOneOf: [ 'pl', 'fr' ],
364 durationMax: 4,
365 nsfw: 'false' as 'false',
366 licenceOneOf: [ 1, 4 ],
367 sort: '-name'
368 }
369
370 const body = await command.advancedVideoSearch({ search: query })
371 expect(body.total).to.equal(4)
372
373 const videos = body.data
374 expect(videos[0].name).to.equal('1111 2222 3333 - 8')
375 expect(videos[1].name).to.equal('1111 2222 3333 - 7')
376 expect(videos[2].name).to.equal('1111 2222 3333 - 6')
377 expect(videos[3].name).to.equal('1111 2222 3333')
378 })
379
380 it('Should make an advanced search and only show the first result', async function () {
381 const query = {
382 search: '1111 2222 3333',
383 languageOneOf: [ 'pl', 'fr' ],
384 durationMax: 4,
385 nsfw: 'false' as 'false',
386 licenceOneOf: [ 1, 4 ],
387 sort: '-name',
388 start: 0,
389 count: 1
390 }
391
392 const body = await command.advancedVideoSearch({ search: query })
393 expect(body.total).to.equal(4)
394
395 const videos = body.data
396 expect(videos[0].name).to.equal('1111 2222 3333 - 8')
397 })
398
399 it('Should make an advanced search and only show the last result', async function () {
400 const query = {
401 search: '1111 2222 3333',
402 languageOneOf: [ 'pl', 'fr' ],
403 durationMax: 4,
404 nsfw: 'false' as 'false',
405 licenceOneOf: [ 1, 4 ],
406 sort: '-name',
407 start: 3,
408 count: 1
409 }
410
411 const body = await command.advancedVideoSearch({ search: query })
412 expect(body.total).to.equal(4)
413
414 const videos = body.data
415 expect(videos[0].name).to.equal('1111 2222 3333')
416 })
417
418 it('Should search on originally published date', async function () {
419 const baseQuery = {
420 search: '1111 2222 3333',
421 languageOneOf: [ 'pl', 'fr' ],
422 durationMax: 4,
423 nsfw: 'false' as 'false',
424 licenceOneOf: [ 1, 4 ]
425 }
426
427 {
428 const query = { ...baseQuery, originallyPublishedStartDate: '2019-02-11T09:58:08.286Z' }
429 const body = await command.advancedVideoSearch({ search: query })
430
431 expect(body.total).to.equal(1)
432 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
433 }
434
435 {
436 const query = { ...baseQuery, originallyPublishedEndDate: '2019-03-11T09:58:08.286Z' }
437 const body = await command.advancedVideoSearch({ search: query })
438
439 expect(body.total).to.equal(1)
440 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
441 }
442
443 {
444 const query = { ...baseQuery, originallyPublishedEndDate: '2019-01-11T09:58:08.286Z' }
445 const body = await command.advancedVideoSearch({ search: query })
446
447 expect(body.total).to.equal(0)
448 }
449
450 {
451 const query = { ...baseQuery, originallyPublishedStartDate: '2019-03-11T09:58:08.286Z' }
452 const body = await command.advancedVideoSearch({ search: query })
453
454 expect(body.total).to.equal(0)
455 }
456
457 {
458 const query = {
459 ...baseQuery,
460 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
461 originallyPublishedEndDate: '2019-01-10T09:58:08.286Z'
462 }
463 const body = await command.advancedVideoSearch({ search: query })
464
465 expect(body.total).to.equal(0)
466 }
467
468 {
469 const query = {
470 ...baseQuery,
471 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
472 originallyPublishedEndDate: '2019-04-11T09:58:08.286Z'
473 }
474 const body = await command.advancedVideoSearch({ search: query })
475
476 expect(body.total).to.equal(1)
477 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
478 }
479 })
480
481 it('Should search by UUID', async function () {
482 const search = videoUUID
483 const body = await command.advancedVideoSearch({ search: { search } })
484
485 expect(body.total).to.equal(1)
486 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
487 })
488
489 it('Should filter by UUIDs', async function () {
490 for (const uuid of [ videoUUID, videoShortUUID ]) {
491 const body = await command.advancedVideoSearch({ search: { uuids: [ uuid ] } })
492
493 expect(body.total).to.equal(1)
494 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
495 }
496
497 {
498 const body = await command.advancedVideoSearch({ search: { uuids: [ 'dfd70b83-639f-4980-94af-304a56ab4b35' ] } })
499
500 expect(body.total).to.equal(0)
501 expect(body.data).to.have.lengthOf(0)
502 }
503 })
504
505 it('Should search by host', async function () {
506 {
507 const body = await command.advancedVideoSearch({ search: { search: '6666 7777 8888', host: server.host } })
508 expect(body.total).to.equal(1)
509 expect(body.data[0].name).to.equal('6666 7777 8888')
510 }
511
512 {
513 const body = await command.advancedVideoSearch({ search: { search: '1111', host: 'example.com' } })
514 expect(body.total).to.equal(0)
515 expect(body.data).to.have.lengthOf(0)
516 }
517
518 {
519 const body = await command.advancedVideoSearch({ search: { search: 'remote', host: remoteServer.host } })
520 expect(body.total).to.equal(2)
521 expect(body.data).to.have.lengthOf(2)
522 expect(body.data[0].name).to.equal('remote video 1')
523 expect(body.data[1].name).to.equal('remote video 2')
524 }
525 })
526
527 it('Should search by live', async function () {
528 this.timeout(120000)
529
530 {
531 const newConfig = {
532 search: {
533 searchIndex: { enabled: false }
534 },
535 live: { enabled: true }
536 }
537 await server.config.updateCustomSubConfig({ newConfig })
538 }
539
540 {
541 const body = await command.advancedVideoSearch({ search: { isLive: true } })
542
543 expect(body.total).to.equal(0)
544 expect(body.data).to.have.lengthOf(0)
545 }
546
547 {
548 const liveCommand = server.live
549
550 const liveAttributes = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.store.channel.id }
551 const live = await liveCommand.create({ fields: liveAttributes })
552
553 const ffmpegCommand = await liveCommand.sendRTMPStreamInVideo({ videoId: live.id })
554 await liveCommand.waitUntilPublished({ videoId: live.id })
555
556 const body = await command.advancedVideoSearch({ search: { isLive: true } })
557
558 expect(body.total).to.equal(1)
559 expect(body.data[0].name).to.equal('live')
560
561 await stopFfmpeg(ffmpegCommand)
562 }
563 })
564
565 after(async function () {
566 await cleanupTests([ server ])
567 })
568})