1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
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'
9 function buildOneMonthAgo () {
10 const monthAgo = new Date()
11 monthAgo.setHours(0, 0, 0, 0)
13 monthAgo.setDate(monthAgo.getDate() - 29)
18 describe('Test views timeserie stats', function () {
19 const availableMetrics: VideoStatsTimeserieMetric[] = [ 'viewers' ]
21 let servers: PeerTubeServer[]
23 before(async function () {
26 servers = await prepareViewsServers()
29 describe('Common metric tests', function () {
30 let vodVideoId: string
32 before(async function () {
35 ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true }))
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 })
42 expect(data).to.have.length.at.least(1)
44 for (const d of data) {
45 expect(d.value).to.equal(0)
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
56 function expectTodayLastValue (result: VideoStatsTimeserie, lastValue?: number) {
57 const { data } = result
59 const last = data[data.length - 1]
60 const today = new Date().getDate()
61 expect(new Date(last.date).getDate()).to.equal(today)
63 if (lastValue) expect(last.value).to.equal(lastValue)
66 function expectTimeserieData (result: VideoStatsTimeserie, lastValue: number) {
67 const { data } = result
68 expect(data).to.have.length.at.least(25)
70 expectTodayLastValue(result, lastValue)
72 for (let i = 0; i < data.length - 2; i++) {
73 expect(data[i].value).to.equal(0)
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)
83 before(async function () {
86 ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true }))
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 ] })
95 await processViewersStats(servers)
97 for (const videoId of [ vodVideoId, liveVideoId ]) {
98 const result = await servers[0].videoStats.getTimeserieStats({
100 startDate: buildOneMonthAgo(),
104 expectTimeserieData(result, 2)
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({
112 startDate: buildOneMonthAgo(),
114 metric: 'aggregateWatchTime'
116 expectTimeserieData(result, 8)
118 await servers[1].views.simulateViewer({ id: videoId, currentTimes: [ 0, 1 ] })
121 await processViewersStats(servers)
123 for (const videoId of [ vodVideoId, liveVideoId ]) {
124 const result = await servers[0].videoStats.getTimeserieStats({
126 startDate: buildOneMonthAgo(),
128 metric: 'aggregateWatchTime'
130 expectTimeserieData(result, 9)
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)
139 const result = await servers[0].videoStats.getTimeserieStats({
141 metric: 'aggregateWatchTime',
142 startDate: twentyDaysAgo,
146 expect(result.groupInterval).to.equal('1 day')
147 expect(result.data).to.have.lengthOf(20)
149 const first = result.data[0]
150 expect(new Date(first.date).toLocaleDateString()).to.equal(twentyDaysAgo.toLocaleDateString())
152 expectInterval(result, 24 * 3600 * 1000)
153 expectTodayLastValue(result, 9)
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)
161 const result = await servers[0].videoStats.getTimeserieStats({
163 metric: 'aggregateWatchTime',
164 startDate: heightYearsAgo,
168 expect(result.groupInterval).to.equal('6 months')
169 expect(result.data).to.have.length.above(10).and.below(200)
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)
177 const result = await servers[0].videoStats.getTimeserieStats({
179 metric: 'aggregateWatchTime',
180 startDate: threeMonthsAgo,
184 expect(result.groupInterval).to.equal('2 days')
185 expect(result.data).to.have.length.above(10).and.below(200)
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)
193 const result = await servers[0].videoStats.getTimeserieStats({
195 metric: 'aggregateWatchTime',
196 startDate: twoDaysAgo,
200 expect(result.groupInterval).to.equal('1 hour')
201 expect(result.data).to.have.length.above(24).and.below(50)
203 expectInterval(result, 3600 * 1000)
204 expectTodayLastValue(result, 9)
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)
212 const result = await servers[0].videoStats.getTimeserieStats({
214 metric: 'aggregateWatchTime',
215 startDate: twoHoursAgo,
219 expect(result.groupInterval).to.equal('10 minutes')
220 expect(result.data).to.have.length.above(20).and.below(30)
222 expectInterval(result, 60 * 10 * 1000)
223 expectTodayLastValue(result)
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)
231 const result = await servers[0].videoStats.getTimeserieStats({
233 metric: 'aggregateWatchTime',
234 startDate: thirtyAgo,
238 expect(result.groupInterval).to.equal('1 minute')
239 expect(result.data).to.have.length.above(20).and.below(40)
241 expectInterval(result, 60 * 1000)
242 expectTodayLastValue(result)
245 after(async function () {
246 await stopFfmpeg(command)
250 after(async function () {
251 await cleanupTests(servers)