1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import * as chai from 'chai'
5 import { FfmpegCommand } from 'fluent-ffmpeg'
6 import { prepareViewsServers, prepareViewsVideos, processViewersStats } from '@server/tests/shared'
7 import { VideoStatsTimeserie, VideoStatsTimeserieMetric } from '@shared/models'
8 import { cleanupTests, PeerTubeServer, stopFfmpeg } from '@shared/server-commands'
10 const expect = chai.expect
12 function buildOneMonthAgo () {
13 const monthAgo = new Date()
14 monthAgo.setHours(0, 0, 0, 0)
16 monthAgo.setDate(monthAgo.getDate() - 29)
21 describe('Test views timeserie stats', function () {
22 const availableMetrics: VideoStatsTimeserieMetric[] = [ 'viewers' ]
24 let servers: PeerTubeServer[]
26 before(async function () {
29 servers = await prepareViewsServers()
32 describe('Common metric tests', function () {
33 let vodVideoId: string
35 before(async function () {
38 ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true }))
41 it('Should display empty metric stats', async function () {
42 for (const metric of availableMetrics) {
43 const { data } = await servers[0].videoStats.getTimeserieStats({ videoId: vodVideoId, metric })
45 expect(data).to.have.length.at.least(1)
47 for (const d of data) {
48 expect(d.value).to.equal(0)
54 describe('Test viewer and watch time metrics on live and VOD', function () {
55 let vodVideoId: string
56 let liveVideoId: string
57 let command: FfmpegCommand
59 function expectTodayLastValue (result: VideoStatsTimeserie, lastValue?: number) {
60 const { data } = result
62 const last = data[data.length - 1]
63 const today = new Date().getDate()
64 expect(new Date(last.date).getDate()).to.equal(today)
66 if (lastValue) expect(last.value).to.equal(lastValue)
69 function expectTimeserieData (result: VideoStatsTimeserie, lastValue: number) {
70 const { data } = result
71 expect(data).to.have.length.at.least(25)
73 expectTodayLastValue(result, lastValue)
75 for (let i = 0; i < data.length - 2; i++) {
76 expect(data[i].value).to.equal(0)
80 function expectInterval (result: VideoStatsTimeserie, intervalMs: number) {
81 const first = result.data[0]
82 const second = result.data[1]
83 expect(new Date(second.date).getTime() - new Date(first.date).getTime()).to.equal(intervalMs)
86 before(async function () {
89 ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true }))
92 it('Should display appropriate viewers metrics', async function () {
93 for (const videoId of [ vodVideoId, liveVideoId ]) {
94 await servers[0].views.simulateViewer({ id: videoId, currentTimes: [ 0, 3 ] })
95 await servers[1].views.simulateViewer({ id: videoId, currentTimes: [ 0, 5 ] })
98 await processViewersStats(servers)
100 for (const videoId of [ vodVideoId, liveVideoId ]) {
101 const result = await servers[0].videoStats.getTimeserieStats({
103 startDate: buildOneMonthAgo(),
107 expectTimeserieData(result, 2)
111 it('Should display appropriate watch time metrics', async function () {
112 for (const videoId of [ vodVideoId, liveVideoId ]) {
113 const result = await servers[0].videoStats.getTimeserieStats({
115 startDate: buildOneMonthAgo(),
117 metric: 'aggregateWatchTime'
119 expectTimeserieData(result, 8)
121 await servers[1].views.simulateViewer({ id: videoId, currentTimes: [ 0, 1 ] })
124 await processViewersStats(servers)
126 for (const videoId of [ vodVideoId, liveVideoId ]) {
127 const result = await servers[0].videoStats.getTimeserieStats({
129 startDate: buildOneMonthAgo(),
131 metric: 'aggregateWatchTime'
133 expectTimeserieData(result, 9)
137 it('Should use a custom start/end date', async function () {
138 const now = new Date()
139 const twentyDaysAgo = new Date()
140 twentyDaysAgo.setDate(twentyDaysAgo.getDate() - 19)
142 const result = await servers[0].videoStats.getTimeserieStats({
144 metric: 'aggregateWatchTime',
145 startDate: twentyDaysAgo,
149 expect(result.groupInterval).to.equal('1 day')
150 expect(result.data).to.have.lengthOf(20)
152 const first = result.data[0]
153 expect(new Date(first.date).toLocaleDateString()).to.equal(twentyDaysAgo.toLocaleDateString())
155 expectInterval(result, 24 * 3600 * 1000)
156 expectTodayLastValue(result, 9)
159 it('Should automatically group by months', async function () {
160 const now = new Date()
161 const heightYearsAgo = new Date()
162 heightYearsAgo.setFullYear(heightYearsAgo.getFullYear() - 7)
164 const result = await servers[0].videoStats.getTimeserieStats({
166 metric: 'aggregateWatchTime',
167 startDate: heightYearsAgo,
171 expect(result.groupInterval).to.equal('6 months')
172 expect(result.data).to.have.length.above(10).and.below(200)
175 it('Should automatically group by days', async function () {
176 const now = new Date()
177 const threeMonthsAgo = new Date()
178 threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3)
180 const result = await servers[0].videoStats.getTimeserieStats({
182 metric: 'aggregateWatchTime',
183 startDate: threeMonthsAgo,
187 expect(result.groupInterval).to.equal('2 days')
188 expect(result.data).to.have.length.above(10).and.below(200)
191 it('Should automatically group by hours', async function () {
192 const now = new Date()
193 const twoDaysAgo = new Date()
194 twoDaysAgo.setDate(twoDaysAgo.getDate() - 1)
196 const result = await servers[0].videoStats.getTimeserieStats({
198 metric: 'aggregateWatchTime',
199 startDate: twoDaysAgo,
203 expect(result.groupInterval).to.equal('1 hour')
204 expect(result.data).to.have.length.above(24).and.below(50)
206 expectInterval(result, 3600 * 1000)
207 expectTodayLastValue(result, 9)
210 it('Should automatically group by ten minutes', async function () {
211 const now = new Date()
212 const twoHoursAgo = new Date()
213 twoHoursAgo.setHours(twoHoursAgo.getHours() - 4)
215 const result = await servers[0].videoStats.getTimeserieStats({
217 metric: 'aggregateWatchTime',
218 startDate: twoHoursAgo,
222 expect(result.groupInterval).to.equal('10 minutes')
223 expect(result.data).to.have.length.above(20).and.below(30)
225 expectInterval(result, 60 * 10 * 1000)
226 expectTodayLastValue(result)
229 it('Should automatically group by one minute', async function () {
230 const now = new Date()
231 const thirtyAgo = new Date()
232 thirtyAgo.setMinutes(thirtyAgo.getMinutes() - 30)
234 const result = await servers[0].videoStats.getTimeserieStats({
236 metric: 'aggregateWatchTime',
237 startDate: thirtyAgo,
241 expect(result.groupInterval).to.equal('1 minute')
242 expect(result.data).to.have.length.above(20).and.below(40)
244 expectInterval(result, 60 * 1000)
245 expectTodayLastValue(result)
248 after(async function () {
249 await stopFfmpeg(command)
253 after(async function () {
254 await cleanupTests(servers)