aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests/api/views/video-views-overall-stats.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-03-24 13:36:47 +0100
committerChocobozzz <chocobozzz@cpy.re>2022-04-15 09:49:35 +0200
commitb211106695bb82f6c32e53306081b5262c3d109d (patch)
treefa187de1c33b0956665f5362e29af6b0f6d8bb57 /server/tests/api/views/video-views-overall-stats.ts
parent69d48ee30c9d47cddf0c3c047dc99a99dcb6e894 (diff)
downloadPeerTube-b211106695bb82f6c32e53306081b5262c3d109d.tar.gz
PeerTube-b211106695bb82f6c32e53306081b5262c3d109d.tar.zst
PeerTube-b211106695bb82f6c32e53306081b5262c3d109d.zip
Support video views/viewers stats in server
* Add "currentTime" and "event" body params to view endpoint * Merge watching and view endpoints * Introduce WatchAction AP activity * Add tables to store viewer information of local videos * Add endpoints to fetch video views/viewers stats of local videos * Refactor views/viewers handlers * Support "views" and "viewers" counters for both VOD and live videos
Diffstat (limited to 'server/tests/api/views/video-views-overall-stats.ts')
-rw-r--r--server/tests/api/views/video-views-overall-stats.ts291
1 files changed, 291 insertions, 0 deletions
diff --git a/server/tests/api/views/video-views-overall-stats.ts b/server/tests/api/views/video-views-overall-stats.ts
new file mode 100644
index 000000000..22761d6ec
--- /dev/null
+++ b/server/tests/api/views/video-views-overall-stats.ts
@@ -0,0 +1,291 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import 'mocha'
4import * as chai from 'chai'
5import { FfmpegCommand } from 'fluent-ffmpeg'
6import { prepareViewsServers, prepareViewsVideos, processViewersStats } from '@server/tests/shared'
7import { cleanupTests, PeerTubeServer, stopFfmpeg, waitJobs } from '@shared/server-commands'
8
9const expect = chai.expect
10
11describe('Test views overall stats', function () {
12 let servers: PeerTubeServer[]
13
14 before(async function () {
15 this.timeout(120000)
16
17 servers = await prepareViewsServers()
18 })
19
20 describe('Test rates and comments of local videos on VOD', function () {
21 let vodVideoId: string
22
23 before(async function () {
24 this.timeout(60000);
25
26 ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true }))
27 })
28
29 it('Should have the appropriate likes', async function () {
30 this.timeout(60000)
31
32 await servers[0].videos.rate({ id: vodVideoId, rating: 'like' })
33 await servers[1].videos.rate({ id: vodVideoId, rating: 'like' })
34
35 await waitJobs(servers)
36
37 const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId })
38
39 expect(stats.likes).to.equal(2)
40 expect(stats.dislikes).to.equal(0)
41 })
42
43 it('Should have the appropriate dislikes', async function () {
44 this.timeout(60000)
45
46 await servers[0].videos.rate({ id: vodVideoId, rating: 'dislike' })
47 await servers[1].videos.rate({ id: vodVideoId, rating: 'dislike' })
48
49 await waitJobs(servers)
50
51 const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId })
52
53 expect(stats.likes).to.equal(0)
54 expect(stats.dislikes).to.equal(2)
55 })
56
57 it('Should have the appropriate comments', async function () {
58 this.timeout(60000)
59
60 await servers[0].comments.createThread({ videoId: vodVideoId, text: 'root' })
61 await servers[0].comments.addReplyToLastThread({ text: 'reply' })
62 await servers[1].comments.createThread({ videoId: vodVideoId, text: 'root' })
63
64 await waitJobs(servers)
65
66 const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId })
67 expect(stats.comments).to.equal(3)
68 })
69 })
70
71 describe('Test watch time stats of local videos on live and VOD', function () {
72 let vodVideoId: string
73 let liveVideoId: string
74 let command: FfmpegCommand
75
76 before(async function () {
77 this.timeout(60000);
78
79 ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true }))
80 })
81
82 it('Should display overall stats of a video with no viewers', async function () {
83 for (const videoId of [ liveVideoId, vodVideoId ]) {
84 const stats = await servers[0].videoStats.getOverallStats({ videoId })
85
86 expect(stats.views).to.equal(0)
87 expect(stats.averageWatchTime).to.equal(0)
88 expect(stats.totalWatchTime).to.equal(0)
89 }
90 })
91
92 it('Should display overall stats with 1 viewer below the watch time limit', async function () {
93 this.timeout(60000)
94
95 for (const videoId of [ liveVideoId, vodVideoId ]) {
96 await servers[0].views.simulateViewer({ id: videoId, currentTimes: [ 0, 1 ] })
97 }
98
99 await processViewersStats(servers)
100
101 for (const videoId of [ liveVideoId, vodVideoId ]) {
102 const stats = await servers[0].videoStats.getOverallStats({ videoId })
103
104 expect(stats.views).to.equal(0)
105 expect(stats.averageWatchTime).to.equal(1)
106 expect(stats.totalWatchTime).to.equal(1)
107 }
108 })
109
110 it('Should display overall stats with 2 viewers', async function () {
111 this.timeout(60000)
112
113 {
114 await servers[0].views.simulateViewer({ id: vodVideoId, currentTimes: [ 0, 3 ] })
115 await servers[0].views.simulateViewer({ id: liveVideoId, currentTimes: [ 0, 35, 40 ] })
116
117 await processViewersStats(servers)
118
119 {
120 const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId })
121 expect(stats.views).to.equal(1)
122 expect(stats.averageWatchTime).to.equal(2)
123 expect(stats.totalWatchTime).to.equal(4)
124 }
125
126 {
127 const stats = await servers[0].videoStats.getOverallStats({ videoId: liveVideoId })
128 expect(stats.views).to.equal(1)
129 expect(stats.averageWatchTime).to.equal(21)
130 expect(stats.totalWatchTime).to.equal(41)
131 }
132 }
133 })
134
135 it('Should display overall stats with a remote viewer below the watch time limit', async function () {
136 this.timeout(60000)
137
138 for (const videoId of [ liveVideoId, vodVideoId ]) {
139 await servers[1].views.simulateViewer({ id: videoId, currentTimes: [ 0, 2 ] })
140 }
141
142 await processViewersStats(servers)
143
144 {
145 const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId })
146
147 expect(stats.views).to.equal(1)
148 expect(stats.averageWatchTime).to.equal(2)
149 expect(stats.totalWatchTime).to.equal(6)
150 }
151
152 {
153 const stats = await servers[0].videoStats.getOverallStats({ videoId: liveVideoId })
154
155 expect(stats.views).to.equal(1)
156 expect(stats.averageWatchTime).to.equal(14)
157 expect(stats.totalWatchTime).to.equal(43)
158 }
159 })
160
161 it('Should display overall stats with a remote viewer above the watch time limit', async function () {
162 this.timeout(60000)
163
164 await servers[1].views.simulateViewer({ id: vodVideoId, currentTimes: [ 0, 5 ] })
165 await servers[1].views.simulateViewer({ id: liveVideoId, currentTimes: [ 0, 45 ] })
166 await processViewersStats(servers)
167
168 {
169 const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId })
170 expect(stats.views).to.equal(2)
171 expect(stats.averageWatchTime).to.equal(3)
172 expect(stats.totalWatchTime).to.equal(11)
173 }
174
175 {
176 const stats = await servers[0].videoStats.getOverallStats({ videoId: liveVideoId })
177 expect(stats.views).to.equal(2)
178 expect(stats.averageWatchTime).to.equal(22)
179 expect(stats.totalWatchTime).to.equal(88)
180 }
181 })
182
183 after(async function () {
184 await stopFfmpeg(command)
185 })
186 })
187
188 describe('Test watchers peak stats of local videos on VOD', function () {
189 let videoUUID: string
190
191 before(async function () {
192 this.timeout(60000);
193
194 ({ vodVideoId: videoUUID } = await prepareViewsVideos({ servers, live: true, vod: true }))
195 })
196
197 it('Should not have watchers peak', async function () {
198 const stats = await servers[0].videoStats.getOverallStats({ videoId: videoUUID })
199
200 expect(stats.viewersPeak).to.equal(0)
201 expect(stats.viewersPeakDate).to.be.null
202 })
203
204 it('Should have watcher peak with 1 watcher', async function () {
205 this.timeout(60000)
206
207 const before = new Date()
208 await servers[0].views.simulateViewer({ id: videoUUID, currentTimes: [ 0, 2 ] })
209 const after = new Date()
210
211 await processViewersStats(servers)
212
213 const stats = await servers[0].videoStats.getOverallStats({ videoId: videoUUID })
214
215 expect(stats.viewersPeak).to.equal(1)
216 expect(new Date(stats.viewersPeakDate)).to.be.above(before).and.below(after)
217 })
218
219 it('Should have watcher peak with 2 watchers', async function () {
220 this.timeout(60000)
221
222 const before = new Date()
223 await servers[0].views.view({ id: videoUUID, currentTime: 0 })
224 await servers[1].views.view({ id: videoUUID, currentTime: 0 })
225 await servers[0].views.view({ id: videoUUID, currentTime: 2 })
226 await servers[1].views.view({ id: videoUUID, currentTime: 2 })
227 const after = new Date()
228
229 await processViewersStats(servers)
230
231 const stats = await servers[0].videoStats.getOverallStats({ videoId: videoUUID })
232
233 expect(stats.viewersPeak).to.equal(2)
234 expect(new Date(stats.viewersPeakDate)).to.be.above(before).and.below(after)
235 })
236 })
237
238 describe('Test countries', function () {
239
240 it('Should not report countries if geoip is disabled', async function () {
241 this.timeout(60000)
242
243 const { uuid } = await servers[0].videos.quickUpload({ name: 'video' })
244 await waitJobs(servers)
245
246 await servers[1].views.view({ id: uuid, xForwardedFor: '8.8.8.8,127.0.0.1', currentTime: 1 })
247
248 await processViewersStats(servers)
249
250 const stats = await servers[0].videoStats.getOverallStats({ videoId: uuid })
251 expect(stats.countries).to.have.lengthOf(0)
252 })
253
254 it('Should report countries if geoip is enabled', async function () {
255 this.timeout(60000)
256
257 const { uuid } = await servers[0].videos.quickUpload({ name: 'video' })
258 await waitJobs(servers)
259
260 await Promise.all([
261 servers[0].kill(),
262 servers[1].kill()
263 ])
264
265 const config = { geo_ip: { enabled: true } }
266 await Promise.all([
267 servers[0].run(config),
268 servers[1].run(config)
269 ])
270
271 await servers[0].views.view({ id: uuid, xForwardedFor: '8.8.8.8,127.0.0.1', currentTime: 1 })
272 await servers[1].views.view({ id: uuid, xForwardedFor: '8.8.8.4,127.0.0.1', currentTime: 3 })
273 await servers[1].views.view({ id: uuid, xForwardedFor: '80.67.169.12,127.0.0.1', currentTime: 2 })
274
275 await processViewersStats(servers)
276
277 const stats = await servers[0].videoStats.getOverallStats({ videoId: uuid })
278 expect(stats.countries).to.have.lengthOf(2)
279
280 expect(stats.countries[0].isoCode).to.equal('US')
281 expect(stats.countries[0].viewers).to.equal(2)
282
283 expect(stats.countries[1].isoCode).to.equal('FR')
284 expect(stats.countries[1].viewers).to.equal(1)
285 })
286 })
287
288 after(async function () {
289 await cleanupTests(servers)
290 })
291})