diff options
-rw-r--r-- | server/controllers/api/config.ts | 2 | ||||
-rw-r--r-- | server/controllers/api/search.ts | 6 | ||||
-rw-r--r-- | server/tests/api/search/index.ts | 2 | ||||
-rw-r--r-- | server/tests/api/search/search-channels.ts | 79 | ||||
-rw-r--r-- | server/tests/api/search/search-index.ts | 252 | ||||
-rw-r--r-- | shared/extra-utils/search/video-channels.ts | 15 |
6 files changed, 351 insertions, 5 deletions
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 1d48b4b26..b80ea4902 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -463,7 +463,7 @@ function customConfig (): CustomConfig { | |||
463 | disableLocalSearch: CONFIG.SEARCH.SEARCH_INDEX.DISABLE_LOCAL_SEARCH, | 463 | disableLocalSearch: CONFIG.SEARCH.SEARCH_INDEX.DISABLE_LOCAL_SEARCH, |
464 | isDefaultSearch: CONFIG.SEARCH.SEARCH_INDEX.IS_DEFAULT_SEARCH | 464 | isDefaultSearch: CONFIG.SEARCH.SEARCH_INDEX.IS_DEFAULT_SEARCH |
465 | } | 465 | } |
466 | }, | 466 | } |
467 | } | 467 | } |
468 | } | 468 | } |
469 | 469 | ||
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index e08e1d79f..1dea77d29 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts | |||
@@ -76,7 +76,7 @@ function searchVideoChannels (req: express.Request, res: express.Response) { | |||
76 | // @username -> username to search in DB | 76 | // @username -> username to search in DB |
77 | if (query.search.startsWith('@')) query.search = query.search.replace(/^@/, '') | 77 | if (query.search.startsWith('@')) query.search = query.search.replace(/^@/, '') |
78 | 78 | ||
79 | if (isSearchIndexEnabled(query)) { | 79 | if (isSearchIndexSearch(query)) { |
80 | return searchVideoChannelsIndex(query, res) | 80 | return searchVideoChannelsIndex(query, res) |
81 | } | 81 | } |
82 | 82 | ||
@@ -157,7 +157,7 @@ function searchVideos (req: express.Request, res: express.Response) { | |||
157 | return searchVideoURI(search, res) | 157 | return searchVideoURI(search, res) |
158 | } | 158 | } |
159 | 159 | ||
160 | if (isSearchIndexEnabled(query)) { | 160 | if (isSearchIndexSearch(query)) { |
161 | return searchVideosIndex(query, res) | 161 | return searchVideosIndex(query, res) |
162 | } | 162 | } |
163 | 163 | ||
@@ -226,7 +226,7 @@ async function searchVideoURI (url: string, res: express.Response) { | |||
226 | }) | 226 | }) |
227 | } | 227 | } |
228 | 228 | ||
229 | function isSearchIndexEnabled (query: SearchTargetQuery) { | 229 | function isSearchIndexSearch (query: SearchTargetQuery) { |
230 | if (query.searchTarget === 'search-index') return true | 230 | if (query.searchTarget === 'search-index') return true |
231 | 231 | ||
232 | const searchIndexConfig = CONFIG.SEARCH.SEARCH_INDEX | 232 | const searchIndexConfig = CONFIG.SEARCH.SEARCH_INDEX |
diff --git a/server/tests/api/search/index.ts b/server/tests/api/search/index.ts index d573c8a9e..232c1f2a4 100644 --- a/server/tests/api/search/index.ts +++ b/server/tests/api/search/index.ts | |||
@@ -1,3 +1,5 @@ | |||
1 | import './search-activitypub-video-channels' | 1 | import './search-activitypub-video-channels' |
2 | import './search-activitypub-videos' | 2 | import './search-activitypub-videos' |
3 | import './search-index' | ||
3 | import './search-videos' | 4 | import './search-videos' |
5 | import './search-channels' | ||
diff --git a/server/tests/api/search/search-channels.ts b/server/tests/api/search/search-channels.ts new file mode 100644 index 000000000..daca2aebe --- /dev/null +++ b/server/tests/api/search/search-channels.ts | |||
@@ -0,0 +1,79 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import 'mocha' | ||
4 | import * as chai from 'chai' | ||
5 | import { searchVideoChannel, advancedVideoChannelSearch } from '@shared/extra-utils/search/video-channels' | ||
6 | import { | ||
7 | addVideoChannel, | ||
8 | cleanupTests, | ||
9 | createUser, | ||
10 | flushAndRunServer, | ||
11 | ServerInfo, | ||
12 | setAccessTokensToServers | ||
13 | } from '../../../../shared/extra-utils' | ||
14 | import { VideoChannel } from '@shared/models' | ||
15 | |||
16 | const expect = chai.expect | ||
17 | |||
18 | describe('Test channels search', function () { | ||
19 | let server: ServerInfo = null | ||
20 | |||
21 | before(async function () { | ||
22 | this.timeout(30000) | ||
23 | |||
24 | server = await flushAndRunServer(1) | ||
25 | |||
26 | await setAccessTokensToServers([ server ]) | ||
27 | |||
28 | { | ||
29 | await createUser({ url: server.url, accessToken: server.accessToken, username: 'user1', password: 'password' }) | ||
30 | const channel = { | ||
31 | name: 'squall_channel', | ||
32 | displayName: 'Squall channel' | ||
33 | } | ||
34 | await addVideoChannel(server.url, server.accessToken, channel) | ||
35 | } | ||
36 | }) | ||
37 | |||
38 | it('Should make a simple search and not have results', async function () { | ||
39 | const res = await searchVideoChannel(server.url, 'abc') | ||
40 | |||
41 | expect(res.body.total).to.equal(0) | ||
42 | expect(res.body.data).to.have.lengthOf(0) | ||
43 | }) | ||
44 | |||
45 | it('Should make a search and have results', async function () { | ||
46 | { | ||
47 | const search = { | ||
48 | search: 'Squall', | ||
49 | start: 0, | ||
50 | count: 1 | ||
51 | } | ||
52 | const res = await advancedVideoChannelSearch(server.url, search) | ||
53 | expect(res.body.total).to.equal(1) | ||
54 | expect(res.body.data).to.have.lengthOf(1) | ||
55 | |||
56 | const channel: VideoChannel = res.body.data[0] | ||
57 | expect(channel.name).to.equal('squall_channel') | ||
58 | expect(channel.displayName).to.equal('Squall channel') | ||
59 | } | ||
60 | |||
61 | { | ||
62 | const search = { | ||
63 | search: 'Squall', | ||
64 | start: 1, | ||
65 | count: 1 | ||
66 | } | ||
67 | |||
68 | const res = await advancedVideoChannelSearch(server.url, search) | ||
69 | |||
70 | expect(res.body.total).to.equal(1) | ||
71 | |||
72 | expect(res.body.data).to.have.lengthOf(0) | ||
73 | } | ||
74 | }) | ||
75 | |||
76 | after(async function () { | ||
77 | await cleanupTests([ server ]) | ||
78 | }) | ||
79 | }) | ||
diff --git a/server/tests/api/search/search-index.ts b/server/tests/api/search/search-index.ts new file mode 100644 index 000000000..2354aaa8b --- /dev/null +++ b/server/tests/api/search/search-index.ts | |||
@@ -0,0 +1,252 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import 'mocha' | ||
4 | import * as chai from 'chai' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | flushAndRunServer, | ||
8 | searchVideo, | ||
9 | ServerInfo, | ||
10 | setAccessTokensToServers, | ||
11 | updateCustomSubConfig, | ||
12 | uploadVideo, | ||
13 | advancedVideosSearch, | ||
14 | immutableAssign | ||
15 | } from '../../../../shared/extra-utils' | ||
16 | import { searchVideoChannel, advancedVideoChannelSearch } from '@shared/extra-utils/search/video-channels' | ||
17 | import { VideosSearchQuery, Video, VideoChannel } from '@shared/models' | ||
18 | |||
19 | const expect = chai.expect | ||
20 | |||
21 | describe('Test videos search', function () { | ||
22 | let server: ServerInfo = null | ||
23 | const localVideoName = 'local video' + new Date().toISOString() | ||
24 | |||
25 | before(async function () { | ||
26 | this.timeout(30000) | ||
27 | |||
28 | server = await flushAndRunServer(1) | ||
29 | |||
30 | await setAccessTokensToServers([ server ]) | ||
31 | |||
32 | await uploadVideo(server.url, server.accessToken, { name: localVideoName }) | ||
33 | }) | ||
34 | |||
35 | describe('Default search', async function () { | ||
36 | |||
37 | it('Should make a local videos search by default', async function () { | ||
38 | this.timeout(10000) | ||
39 | |||
40 | await updateCustomSubConfig(server.url, server.accessToken, { | ||
41 | search: { | ||
42 | searchIndex: { | ||
43 | enabled: true, | ||
44 | isDefaultSearch: false, | ||
45 | disableLocalSearch: false | ||
46 | } | ||
47 | } | ||
48 | }) | ||
49 | |||
50 | const res = await searchVideo(server.url, 'local video') | ||
51 | |||
52 | expect(res.body.total).to.equal(1) | ||
53 | expect(res.body.data[0].name).to.equal(localVideoName) | ||
54 | }) | ||
55 | |||
56 | it('Should make a local channels search by default', async function () { | ||
57 | const res = await searchVideoChannel(server.url, 'root') | ||
58 | |||
59 | expect(res.body.total).to.equal(1) | ||
60 | expect(res.body.data[0].name).to.equal('root_channel') | ||
61 | expect(res.body.data[0].host).to.equal('localhost:' + server.port) | ||
62 | }) | ||
63 | |||
64 | it('Should make an index videos search by default', async function () { | ||
65 | await updateCustomSubConfig(server.url, server.accessToken, { | ||
66 | search: { | ||
67 | searchIndex: { | ||
68 | enabled: true, | ||
69 | isDefaultSearch: true, | ||
70 | disableLocalSearch: false | ||
71 | } | ||
72 | } | ||
73 | }) | ||
74 | |||
75 | const res = await searchVideo(server.url, 'local video') | ||
76 | expect(res.body.total).to.be.greaterThan(2) | ||
77 | }) | ||
78 | |||
79 | it('Should make an index channels search by default', async function () { | ||
80 | const res = await searchVideoChannel(server.url, 'root') | ||
81 | expect(res.body.total).to.be.greaterThan(2) | ||
82 | }) | ||
83 | |||
84 | it('Should make an index videos search if local search is disabled', async function () { | ||
85 | await updateCustomSubConfig(server.url, server.accessToken, { | ||
86 | search: { | ||
87 | searchIndex: { | ||
88 | enabled: true, | ||
89 | isDefaultSearch: false, | ||
90 | disableLocalSearch: true | ||
91 | } | ||
92 | } | ||
93 | }) | ||
94 | |||
95 | const res = await searchVideo(server.url, 'local video') | ||
96 | expect(res.body.total).to.be.greaterThan(2) | ||
97 | }) | ||
98 | |||
99 | it('Should make an index channels search if local search is disabled', async function () { | ||
100 | const res = await searchVideoChannel(server.url, 'root') | ||
101 | expect(res.body.total).to.be.greaterThan(2) | ||
102 | }) | ||
103 | }) | ||
104 | |||
105 | describe('Videos search', async function () { | ||
106 | |||
107 | it('Should make a simple search and not have results', async function () { | ||
108 | const res = await searchVideo(server.url, 'a'.repeat(500)) | ||
109 | |||
110 | expect(res.body.total).to.equal(0) | ||
111 | expect(res.body.data).to.have.lengthOf(0) | ||
112 | }) | ||
113 | |||
114 | it('Should make a simple search and have results', async function () { | ||
115 | const res = await searchVideo(server.url, 'What is PeerTube') | ||
116 | |||
117 | expect(res.body.total).to.be.greaterThan(1) | ||
118 | }) | ||
119 | |||
120 | it('Should make a complex search', async function () { | ||
121 | |||
122 | async function check (search: VideosSearchQuery, exists = true) { | ||
123 | const res = await advancedVideosSearch(server.url, search) | ||
124 | |||
125 | if (exists === false) { | ||
126 | expect(res.body.total).to.equal(0) | ||
127 | expect(res.body.data).to.have.lengthOf(0) | ||
128 | return | ||
129 | } | ||
130 | |||
131 | expect(res.body.total).to.equal(1) | ||
132 | expect(res.body.data).to.have.lengthOf(1) | ||
133 | |||
134 | const video: Video = res.body.data[0] | ||
135 | |||
136 | expect(video.name).to.equal('What is PeerTube?') | ||
137 | expect(video.category.label).to.equal('Science & Technology') | ||
138 | expect(video.licence.label).to.equal('Attribution - Share Alike') | ||
139 | expect(video.privacy.label).to.equal('Public') | ||
140 | expect(video.duration).to.equal(113) | ||
141 | expect(video.thumbnailUrl.startsWith('https://framatube.org/static/thumbnails')).to.be.true | ||
142 | |||
143 | expect(video.account.host).to.equal('framatube.org') | ||
144 | expect(video.account.name).to.equal('framasoft') | ||
145 | expect(video.account.url).to.equal('https://framatube.org/accounts/framasoft') | ||
146 | expect(video.account.avatar).to.exist | ||
147 | |||
148 | expect(video.channel.host).to.equal('framatube.org') | ||
149 | expect(video.channel.name).to.equal('bf54d359-cfad-4935-9d45-9d6be93f63e8') | ||
150 | expect(video.channel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8') | ||
151 | expect(video.channel.avatar).to.exist | ||
152 | } | ||
153 | |||
154 | const baseSearch: VideosSearchQuery = { | ||
155 | search: 'what is peertube', | ||
156 | start: 0, | ||
157 | count: 2, | ||
158 | categoryOneOf: [ 15 ], | ||
159 | licenceOneOf: [ 2 ], | ||
160 | tagsAllOf: [ 'framasoft', 'peertube' ], | ||
161 | startDate: '2018-10-01T10:50:46.396Z', | ||
162 | endDate: '2018-10-01T10:55:46.396Z' | ||
163 | } | ||
164 | |||
165 | { | ||
166 | await check(baseSearch) | ||
167 | } | ||
168 | |||
169 | { | ||
170 | const search = immutableAssign(baseSearch, { startDate: '2018-10-01T10:54:46.396Z' }) | ||
171 | await check(search, false) | ||
172 | } | ||
173 | |||
174 | { | ||
175 | const search = immutableAssign(baseSearch, { tagsAllOf: [ 'toto', 'framasoft' ] }) | ||
176 | await check(search, false) | ||
177 | } | ||
178 | |||
179 | { | ||
180 | const search = immutableAssign(baseSearch, { durationMin: 2000 }) | ||
181 | await check(search, false) | ||
182 | } | ||
183 | |||
184 | { | ||
185 | const search = immutableAssign(baseSearch, { nsfw: 'true' }) | ||
186 | await check(search, false) | ||
187 | } | ||
188 | |||
189 | { | ||
190 | const search = immutableAssign(baseSearch, { nsfw: 'false' }) | ||
191 | await check(search, true) | ||
192 | } | ||
193 | |||
194 | { | ||
195 | const search = immutableAssign(baseSearch, { nsfw: 'both' }) | ||
196 | await check(search, true) | ||
197 | } | ||
198 | }) | ||
199 | |||
200 | it('Should have a correct pagination', async function () { | ||
201 | const search = { | ||
202 | search: 'video', | ||
203 | start: 0, | ||
204 | count: 5 | ||
205 | } | ||
206 | |||
207 | const res = await advancedVideosSearch(server.url, search) | ||
208 | |||
209 | expect(res.body.total).to.be.greaterThan(5) | ||
210 | expect(res.body.data).to.have.lengthOf(5) | ||
211 | }) | ||
212 | }) | ||
213 | |||
214 | describe('Channels search', async function () { | ||
215 | |||
216 | it('Should make a simple search and not have results', async function () { | ||
217 | const res = await searchVideoChannel(server.url, 'a'.repeat(500)) | ||
218 | |||
219 | expect(res.body.total).to.equal(0) | ||
220 | expect(res.body.data).to.have.lengthOf(0) | ||
221 | }) | ||
222 | |||
223 | it('Should make a search and have results', async function () { | ||
224 | const res = await advancedVideoChannelSearch(server.url, { search: 'Framasoft', sort: 'createdAt' }) | ||
225 | |||
226 | expect(res.body.total).to.be.greaterThan(0) | ||
227 | expect(res.body.data).to.have.length.greaterThan(0) | ||
228 | |||
229 | const videoChannel: VideoChannel = res.body.data[0] | ||
230 | expect(videoChannel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8') | ||
231 | expect(videoChannel.host).to.equal('framatube.org') | ||
232 | expect(videoChannel.avatar).to.exist | ||
233 | expect(videoChannel.displayName).to.exist | ||
234 | |||
235 | expect(videoChannel.ownerAccount.url).to.equal('https://framatube.org/accounts/framasoft') | ||
236 | expect(videoChannel.ownerAccount.name).to.equal('framasoft') | ||
237 | expect(videoChannel.ownerAccount.host).to.equal('framatube.org') | ||
238 | expect(videoChannel.ownerAccount.avatar).to.exist | ||
239 | }) | ||
240 | |||
241 | it('Should have a correct pagination', async function () { | ||
242 | const res = await advancedVideoChannelSearch(server.url, { search: 'root', start: 0, count: 2 }) | ||
243 | |||
244 | expect(res.body.total).to.be.greaterThan(2) | ||
245 | expect(res.body.data).to.have.lengthOf(2) | ||
246 | }) | ||
247 | }) | ||
248 | |||
249 | after(async function () { | ||
250 | await cleanupTests([ server ]) | ||
251 | }) | ||
252 | }) | ||
diff --git a/shared/extra-utils/search/video-channels.ts b/shared/extra-utils/search/video-channels.ts index 0532134ae..d16210530 100644 --- a/shared/extra-utils/search/video-channels.ts +++ b/shared/extra-utils/search/video-channels.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import { VideoChannelsSearchQuery } from '@shared/models' | ||
1 | import { makeGetRequest } from '../requests/requests' | 2 | import { makeGetRequest } from '../requests/requests' |
2 | 3 | ||
3 | function searchVideoChannel (url: string, search: string, token?: string, statusCodeExpected = 200) { | 4 | function searchVideoChannel (url: string, search: string, token?: string, statusCodeExpected = 200) { |
@@ -15,8 +16,20 @@ function searchVideoChannel (url: string, search: string, token?: string, status | |||
15 | }) | 16 | }) |
16 | } | 17 | } |
17 | 18 | ||
19 | function advancedVideoChannelSearch (url: string, search: VideoChannelsSearchQuery) { | ||
20 | const path = '/api/v1/search/video-channels' | ||
21 | |||
22 | return makeGetRequest({ | ||
23 | url, | ||
24 | path, | ||
25 | query: search, | ||
26 | statusCodeExpected: 200 | ||
27 | }) | ||
28 | } | ||
29 | |||
18 | // --------------------------------------------------------------------------- | 30 | // --------------------------------------------------------------------------- |
19 | 31 | ||
20 | export { | 32 | export { |
21 | searchVideoChannel | 33 | searchVideoChannel, |
34 | advancedVideoChannelSearch | ||
22 | } | 35 | } |