]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/views/video-views-timeserie-stats.ts
Merge branch 'release/4.3.0' into develop
[github/Chocobozzz/PeerTube.git] / server / tests / api / views / video-views-timeserie-stats.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import { expect } from 'chai'
4 import { FfmpegCommand } from 'fluent-ffmpeg'
5 import { prepareViewsServers, prepareViewsVideos, processViewersStats } from '@server/tests/shared'
6 import { VideoStatsTimeserie, VideoStatsTimeserieMetric } from '@shared/models'
7 import { cleanupTests, PeerTubeServer, stopFfmpeg } from '@shared/server-commands'
8
9 function buildOneMonthAgo () {
10 const monthAgo = new Date()
11 monthAgo.setHours(0, 0, 0, 0)
12
13 monthAgo.setDate(monthAgo.getDate() - 29)
14
15 return monthAgo
16 }
17
18 describe('Test views timeserie stats', function () {
19 const availableMetrics: VideoStatsTimeserieMetric[] = [ 'viewers' ]
20
21 let servers: PeerTubeServer[]
22
23 before(async function () {
24 this.timeout(120000)
25
26 servers = await prepareViewsServers()
27 })
28
29 describe('Common metric tests', function () {
30 let vodVideoId: string
31
32 before(async function () {
33 this.timeout(240000);
34
35 ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true }))
36 })
37
38 it('Should display empty metric stats', async function () {
39 for (const metric of availableMetrics) {
40 const { data } = await servers[0].videoStats.getTimeserieStats({ videoId: vodVideoId, metric })
41
42 expect(data).to.have.length.at.least(1)
43
44 for (const d of data) {
45 expect(d.value).to.equal(0)
46 }
47 }
48 })
49 })
50
51 describe('Test viewer and watch time metrics on live and VOD', function () {
52 let vodVideoId: string
53 let liveVideoId: string
54 let command: FfmpegCommand
55
56 function expectTodayLastValue (result: VideoStatsTimeserie, lastValue?: number) {
57 const { data } = result
58
59 const last = data[data.length - 1]
60 const today = new Date().getDate()
61 expect(new Date(last.date).getDate()).to.equal(today)
62
63 if (lastValue) expect(last.value).to.equal(lastValue)
64 }
65
66 function expectTimeserieData (result: VideoStatsTimeserie, lastValue: number) {
67 const { data } = result
68 expect(data).to.have.length.at.least(25)
69
70 expectTodayLastValue(result, lastValue)
71
72 for (let i = 0; i < data.length - 2; i++) {
73 expect(data[i].value).to.equal(0)
74 }
75 }
76
77 function expectInterval (result: VideoStatsTimeserie, intervalMs: number) {
78 const first = result.data[0]
79 const second = result.data[1]
80 expect(new Date(second.date).getTime() - new Date(first.date).getTime()).to.equal(intervalMs)
81 }
82
83 before(async function () {
84 this.timeout(240000);
85
86 ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true }))
87 })
88
89 it('Should display appropriate viewers metrics', async function () {
90 for (const videoId of [ vodVideoId, liveVideoId ]) {
91 await servers[0].views.simulateViewer({ id: videoId, currentTimes: [ 0, 3 ] })
92 await servers[1].views.simulateViewer({ id: videoId, currentTimes: [ 0, 5 ] })
93 }
94
95 await processViewersStats(servers)
96
97 for (const videoId of [ vodVideoId, liveVideoId ]) {
98 const result = await servers[0].videoStats.getTimeserieStats({
99 videoId,
100 startDate: buildOneMonthAgo(),
101 endDate: new Date(),
102 metric: 'viewers'
103 })
104 expectTimeserieData(result, 2)
105 }
106 })
107
108 it('Should display appropriate watch time metrics', async function () {
109 for (const videoId of [ vodVideoId, liveVideoId ]) {
110 const result = await servers[0].videoStats.getTimeserieStats({
111 videoId,
112 startDate: buildOneMonthAgo(),
113 endDate: new Date(),
114 metric: 'aggregateWatchTime'
115 })
116 expectTimeserieData(result, 8)
117
118 await servers[1].views.simulateViewer({ id: videoId, currentTimes: [ 0, 1 ] })
119 }
120
121 await processViewersStats(servers)
122
123 for (const videoId of [ vodVideoId, liveVideoId ]) {
124 const result = await servers[0].videoStats.getTimeserieStats({
125 videoId,
126 startDate: buildOneMonthAgo(),
127 endDate: new Date(),
128 metric: 'aggregateWatchTime'
129 })
130 expectTimeserieData(result, 9)
131 }
132 })
133
134 it('Should use a custom start/end date', async function () {
135 const now = new Date()
136 const twentyDaysAgo = new Date()
137 twentyDaysAgo.setDate(twentyDaysAgo.getDate() - 19)
138
139 const result = await servers[0].videoStats.getTimeserieStats({
140 videoId: vodVideoId,
141 metric: 'aggregateWatchTime',
142 startDate: twentyDaysAgo,
143 endDate: now
144 })
145
146 expect(result.groupInterval).to.equal('1 day')
147 expect(result.data).to.have.lengthOf(20)
148
149 const first = result.data[0]
150 expect(new Date(first.date).toLocaleDateString()).to.equal(twentyDaysAgo.toLocaleDateString())
151
152 expectInterval(result, 24 * 3600 * 1000)
153 expectTodayLastValue(result, 9)
154 })
155
156 it('Should automatically group by months', async function () {
157 const now = new Date()
158 const heightYearsAgo = new Date()
159 heightYearsAgo.setFullYear(heightYearsAgo.getFullYear() - 7)
160
161 const result = await servers[0].videoStats.getTimeserieStats({
162 videoId: vodVideoId,
163 metric: 'aggregateWatchTime',
164 startDate: heightYearsAgo,
165 endDate: now
166 })
167
168 expect(result.groupInterval).to.equal('6 months')
169 expect(result.data).to.have.length.above(10).and.below(200)
170 })
171
172 it('Should automatically group by days', async function () {
173 const now = new Date()
174 const threeMonthsAgo = new Date()
175 threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3)
176
177 const result = await servers[0].videoStats.getTimeserieStats({
178 videoId: vodVideoId,
179 metric: 'aggregateWatchTime',
180 startDate: threeMonthsAgo,
181 endDate: now
182 })
183
184 expect(result.groupInterval).to.equal('2 days')
185 expect(result.data).to.have.length.above(10).and.below(200)
186 })
187
188 it('Should automatically group by hours', async function () {
189 const now = new Date()
190 const twoDaysAgo = new Date()
191 twoDaysAgo.setDate(twoDaysAgo.getDate() - 1)
192
193 const result = await servers[0].videoStats.getTimeserieStats({
194 videoId: vodVideoId,
195 metric: 'aggregateWatchTime',
196 startDate: twoDaysAgo,
197 endDate: now
198 })
199
200 expect(result.groupInterval).to.equal('1 hour')
201 expect(result.data).to.have.length.above(24).and.below(50)
202
203 expectInterval(result, 3600 * 1000)
204 expectTodayLastValue(result, 9)
205 })
206
207 it('Should automatically group by ten minutes', async function () {
208 const now = new Date()
209 const twoHoursAgo = new Date()
210 twoHoursAgo.setHours(twoHoursAgo.getHours() - 4)
211
212 const result = await servers[0].videoStats.getTimeserieStats({
213 videoId: vodVideoId,
214 metric: 'aggregateWatchTime',
215 startDate: twoHoursAgo,
216 endDate: now
217 })
218
219 expect(result.groupInterval).to.equal('10 minutes')
220 expect(result.data).to.have.length.above(20).and.below(30)
221
222 expectInterval(result, 60 * 10 * 1000)
223 expectTodayLastValue(result)
224 })
225
226 it('Should automatically group by one minute', async function () {
227 const now = new Date()
228 const thirtyAgo = new Date()
229 thirtyAgo.setMinutes(thirtyAgo.getMinutes() - 30)
230
231 const result = await servers[0].videoStats.getTimeserieStats({
232 videoId: vodVideoId,
233 metric: 'aggregateWatchTime',
234 startDate: thirtyAgo,
235 endDate: now
236 })
237
238 expect(result.groupInterval).to.equal('1 minute')
239 expect(result.data).to.have.length.above(20).and.below(40)
240
241 expectInterval(result, 60 * 1000)
242 expectTodayLastValue(result)
243 })
244
245 after(async function () {
246 await stopFfmpeg(command)
247 })
248 })
249
250 after(async function () {
251 await cleanupTests(servers)
252 })
253 })