From b211106695bb82f6c32e53306081b5262c3d109d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 24 Mar 2022 13:36:47 +0100 Subject: 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 --- server/tests/api/views/index.ts | 5 + server/tests/api/views/video-views-counter.ts | 155 +++++++++++ .../tests/api/views/video-views-overall-stats.ts | 291 +++++++++++++++++++++ .../tests/api/views/video-views-retention-stats.ts | 56 ++++ .../tests/api/views/video-views-timeserie-stats.ts | 109 ++++++++ server/tests/api/views/videos-views-cleaner.ts | 101 +++++++ 6 files changed, 717 insertions(+) create mode 100644 server/tests/api/views/index.ts create mode 100644 server/tests/api/views/video-views-counter.ts create mode 100644 server/tests/api/views/video-views-overall-stats.ts create mode 100644 server/tests/api/views/video-views-retention-stats.ts create mode 100644 server/tests/api/views/video-views-timeserie-stats.ts create mode 100644 server/tests/api/views/videos-views-cleaner.ts (limited to 'server/tests/api/views') diff --git a/server/tests/api/views/index.ts b/server/tests/api/views/index.ts new file mode 100644 index 000000000..5e06b31fb --- /dev/null +++ b/server/tests/api/views/index.ts @@ -0,0 +1,5 @@ +export * from './video-views-counter' +export * from './video-views-overall-stats' +export * from './video-views-retention-stats' +export * from './video-views-timeserie-stats' +export * from './videos-views-cleaner' diff --git a/server/tests/api/views/video-views-counter.ts b/server/tests/api/views/video-views-counter.ts new file mode 100644 index 000000000..b68aaa350 --- /dev/null +++ b/server/tests/api/views/video-views-counter.ts @@ -0,0 +1,155 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import 'mocha' +import * as chai from 'chai' +import { FfmpegCommand } from 'fluent-ffmpeg' +import { prepareViewsServers, prepareViewsVideos, processViewsBuffer } from '@server/tests/shared' +import { wait } from '@shared/core-utils' +import { cleanupTests, PeerTubeServer, stopFfmpeg, waitJobs } from '@shared/server-commands' + +const expect = chai.expect + +describe('Test video views/viewers counters', function () { + let servers: PeerTubeServer[] + + async function checkCounter (field: 'views' | 'viewers', id: string, expected: number) { + for (const server of servers) { + const video = await server.videos.get({ id }) + + const messageSuffix = video.isLive + ? 'live video' + : 'vod video' + + expect(video[field]).to.equal(expected, `${field} not valid on server ${server.serverNumber} for ${messageSuffix} ${video.uuid}`) + } + } + + before(async function () { + this.timeout(120000) + + servers = await prepareViewsServers() + }) + + describe('Test views counter on VOD', function () { + let videoUUID: string + + before(async function () { + this.timeout(30000) + + const { uuid } = await servers[0].videos.quickUpload({ name: 'video' }) + videoUUID = uuid + + await waitJobs(servers) + }) + + it('Should not view a video if watch time is below the threshold', async function () { + await servers[0].views.simulateViewer({ id: videoUUID, currentTimes: [ 1, 2 ] }) + await processViewsBuffer(servers) + + await checkCounter('views', videoUUID, 0) + }) + + it('Should view a video if watch time is above the threshold', async function () { + await servers[0].views.simulateViewer({ id: videoUUID, currentTimes: [ 1, 4 ] }) + await processViewsBuffer(servers) + + await checkCounter('views', videoUUID, 1) + }) + + it('Should not view again this video with the same IP', async function () { + await servers[0].views.simulateViewer({ id: videoUUID, currentTimes: [ 1, 4 ] }) + await processViewsBuffer(servers) + + await checkCounter('views', videoUUID, 1) + }) + + it('Should view the video from server 2 and send the event', async function () { + await servers[1].views.simulateViewer({ id: videoUUID, currentTimes: [ 1, 4 ] }) + await waitJobs(servers) + await processViewsBuffer(servers) + + await checkCounter('views', videoUUID, 2) + }) + }) + + describe('Test views and viewers counters on live and VOD', function () { + let liveVideoId: string + let vodVideoId: string + let command: FfmpegCommand + + before(async function () { + this.timeout(60000); + + ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true })) + }) + + it('Should display no views and viewers', async function () { + await checkCounter('views', liveVideoId, 0) + await checkCounter('viewers', liveVideoId, 0) + + await checkCounter('views', vodVideoId, 0) + await checkCounter('viewers', vodVideoId, 0) + }) + + it('Should view twice and display 1 view/viewer', async function () { + this.timeout(30000) + + await servers[0].views.simulateViewer({ id: liveVideoId, currentTimes: [ 0, 35 ] }) + await servers[0].views.simulateViewer({ id: liveVideoId, currentTimes: [ 0, 35 ] }) + await servers[0].views.simulateViewer({ id: vodVideoId, currentTimes: [ 0, 5 ] }) + await servers[0].views.simulateViewer({ id: vodVideoId, currentTimes: [ 0, 5 ] }) + + await waitJobs(servers) + await checkCounter('viewers', liveVideoId, 1) + await checkCounter('viewers', vodVideoId, 1) + + await processViewsBuffer(servers) + + await checkCounter('views', liveVideoId, 1) + await checkCounter('views', vodVideoId, 1) + }) + + it('Should wait and display 0 viewers but still have 1 view', async function () { + this.timeout(30000) + + await wait(12000) + await waitJobs(servers) + + await checkCounter('views', liveVideoId, 1) + await checkCounter('viewers', liveVideoId, 0) + + await checkCounter('views', vodVideoId, 1) + await checkCounter('viewers', vodVideoId, 0) + }) + + it('Should view on a remote and on local and display 2 viewers and 3 views', async function () { + this.timeout(30000) + + await servers[0].views.simulateViewer({ id: vodVideoId, currentTimes: [ 0, 5 ] }) + await servers[1].views.simulateViewer({ id: vodVideoId, currentTimes: [ 0, 5 ] }) + await servers[1].views.simulateViewer({ id: vodVideoId, currentTimes: [ 0, 5 ] }) + + await servers[0].views.simulateViewer({ id: liveVideoId, currentTimes: [ 0, 35 ] }) + await servers[1].views.simulateViewer({ id: liveVideoId, currentTimes: [ 0, 35 ] }) + await servers[1].views.simulateViewer({ id: liveVideoId, currentTimes: [ 0, 35 ] }) + + await waitJobs(servers) + + await checkCounter('viewers', liveVideoId, 2) + await checkCounter('viewers', vodVideoId, 2) + + await processViewsBuffer(servers) + + await checkCounter('views', liveVideoId, 3) + await checkCounter('views', vodVideoId, 3) + }) + + after(async function () { + await stopFfmpeg(command) + }) + }) + + after(async function () { + await cleanupTests(servers) + }) +}) 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 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import 'mocha' +import * as chai from 'chai' +import { FfmpegCommand } from 'fluent-ffmpeg' +import { prepareViewsServers, prepareViewsVideos, processViewersStats } from '@server/tests/shared' +import { cleanupTests, PeerTubeServer, stopFfmpeg, waitJobs } from '@shared/server-commands' + +const expect = chai.expect + +describe('Test views overall stats', function () { + let servers: PeerTubeServer[] + + before(async function () { + this.timeout(120000) + + servers = await prepareViewsServers() + }) + + describe('Test rates and comments of local videos on VOD', function () { + let vodVideoId: string + + before(async function () { + this.timeout(60000); + + ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true })) + }) + + it('Should have the appropriate likes', async function () { + this.timeout(60000) + + await servers[0].videos.rate({ id: vodVideoId, rating: 'like' }) + await servers[1].videos.rate({ id: vodVideoId, rating: 'like' }) + + await waitJobs(servers) + + const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId }) + + expect(stats.likes).to.equal(2) + expect(stats.dislikes).to.equal(0) + }) + + it('Should have the appropriate dislikes', async function () { + this.timeout(60000) + + await servers[0].videos.rate({ id: vodVideoId, rating: 'dislike' }) + await servers[1].videos.rate({ id: vodVideoId, rating: 'dislike' }) + + await waitJobs(servers) + + const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId }) + + expect(stats.likes).to.equal(0) + expect(stats.dislikes).to.equal(2) + }) + + it('Should have the appropriate comments', async function () { + this.timeout(60000) + + await servers[0].comments.createThread({ videoId: vodVideoId, text: 'root' }) + await servers[0].comments.addReplyToLastThread({ text: 'reply' }) + await servers[1].comments.createThread({ videoId: vodVideoId, text: 'root' }) + + await waitJobs(servers) + + const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId }) + expect(stats.comments).to.equal(3) + }) + }) + + describe('Test watch time stats of local videos on live and VOD', function () { + let vodVideoId: string + let liveVideoId: string + let command: FfmpegCommand + + before(async function () { + this.timeout(60000); + + ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true })) + }) + + it('Should display overall stats of a video with no viewers', async function () { + for (const videoId of [ liveVideoId, vodVideoId ]) { + const stats = await servers[0].videoStats.getOverallStats({ videoId }) + + expect(stats.views).to.equal(0) + expect(stats.averageWatchTime).to.equal(0) + expect(stats.totalWatchTime).to.equal(0) + } + }) + + it('Should display overall stats with 1 viewer below the watch time limit', async function () { + this.timeout(60000) + + for (const videoId of [ liveVideoId, vodVideoId ]) { + await servers[0].views.simulateViewer({ id: videoId, currentTimes: [ 0, 1 ] }) + } + + await processViewersStats(servers) + + for (const videoId of [ liveVideoId, vodVideoId ]) { + const stats = await servers[0].videoStats.getOverallStats({ videoId }) + + expect(stats.views).to.equal(0) + expect(stats.averageWatchTime).to.equal(1) + expect(stats.totalWatchTime).to.equal(1) + } + }) + + it('Should display overall stats with 2 viewers', async function () { + this.timeout(60000) + + { + await servers[0].views.simulateViewer({ id: vodVideoId, currentTimes: [ 0, 3 ] }) + await servers[0].views.simulateViewer({ id: liveVideoId, currentTimes: [ 0, 35, 40 ] }) + + await processViewersStats(servers) + + { + const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId }) + expect(stats.views).to.equal(1) + expect(stats.averageWatchTime).to.equal(2) + expect(stats.totalWatchTime).to.equal(4) + } + + { + const stats = await servers[0].videoStats.getOverallStats({ videoId: liveVideoId }) + expect(stats.views).to.equal(1) + expect(stats.averageWatchTime).to.equal(21) + expect(stats.totalWatchTime).to.equal(41) + } + } + }) + + it('Should display overall stats with a remote viewer below the watch time limit', async function () { + this.timeout(60000) + + for (const videoId of [ liveVideoId, vodVideoId ]) { + await servers[1].views.simulateViewer({ id: videoId, currentTimes: [ 0, 2 ] }) + } + + await processViewersStats(servers) + + { + const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId }) + + expect(stats.views).to.equal(1) + expect(stats.averageWatchTime).to.equal(2) + expect(stats.totalWatchTime).to.equal(6) + } + + { + const stats = await servers[0].videoStats.getOverallStats({ videoId: liveVideoId }) + + expect(stats.views).to.equal(1) + expect(stats.averageWatchTime).to.equal(14) + expect(stats.totalWatchTime).to.equal(43) + } + }) + + it('Should display overall stats with a remote viewer above the watch time limit', async function () { + this.timeout(60000) + + await servers[1].views.simulateViewer({ id: vodVideoId, currentTimes: [ 0, 5 ] }) + await servers[1].views.simulateViewer({ id: liveVideoId, currentTimes: [ 0, 45 ] }) + await processViewersStats(servers) + + { + const stats = await servers[0].videoStats.getOverallStats({ videoId: vodVideoId }) + expect(stats.views).to.equal(2) + expect(stats.averageWatchTime).to.equal(3) + expect(stats.totalWatchTime).to.equal(11) + } + + { + const stats = await servers[0].videoStats.getOverallStats({ videoId: liveVideoId }) + expect(stats.views).to.equal(2) + expect(stats.averageWatchTime).to.equal(22) + expect(stats.totalWatchTime).to.equal(88) + } + }) + + after(async function () { + await stopFfmpeg(command) + }) + }) + + describe('Test watchers peak stats of local videos on VOD', function () { + let videoUUID: string + + before(async function () { + this.timeout(60000); + + ({ vodVideoId: videoUUID } = await prepareViewsVideos({ servers, live: true, vod: true })) + }) + + it('Should not have watchers peak', async function () { + const stats = await servers[0].videoStats.getOverallStats({ videoId: videoUUID }) + + expect(stats.viewersPeak).to.equal(0) + expect(stats.viewersPeakDate).to.be.null + }) + + it('Should have watcher peak with 1 watcher', async function () { + this.timeout(60000) + + const before = new Date() + await servers[0].views.simulateViewer({ id: videoUUID, currentTimes: [ 0, 2 ] }) + const after = new Date() + + await processViewersStats(servers) + + const stats = await servers[0].videoStats.getOverallStats({ videoId: videoUUID }) + + expect(stats.viewersPeak).to.equal(1) + expect(new Date(stats.viewersPeakDate)).to.be.above(before).and.below(after) + }) + + it('Should have watcher peak with 2 watchers', async function () { + this.timeout(60000) + + const before = new Date() + await servers[0].views.view({ id: videoUUID, currentTime: 0 }) + await servers[1].views.view({ id: videoUUID, currentTime: 0 }) + await servers[0].views.view({ id: videoUUID, currentTime: 2 }) + await servers[1].views.view({ id: videoUUID, currentTime: 2 }) + const after = new Date() + + await processViewersStats(servers) + + const stats = await servers[0].videoStats.getOverallStats({ videoId: videoUUID }) + + expect(stats.viewersPeak).to.equal(2) + expect(new Date(stats.viewersPeakDate)).to.be.above(before).and.below(after) + }) + }) + + describe('Test countries', function () { + + it('Should not report countries if geoip is disabled', async function () { + this.timeout(60000) + + const { uuid } = await servers[0].videos.quickUpload({ name: 'video' }) + await waitJobs(servers) + + await servers[1].views.view({ id: uuid, xForwardedFor: '8.8.8.8,127.0.0.1', currentTime: 1 }) + + await processViewersStats(servers) + + const stats = await servers[0].videoStats.getOverallStats({ videoId: uuid }) + expect(stats.countries).to.have.lengthOf(0) + }) + + it('Should report countries if geoip is enabled', async function () { + this.timeout(60000) + + const { uuid } = await servers[0].videos.quickUpload({ name: 'video' }) + await waitJobs(servers) + + await Promise.all([ + servers[0].kill(), + servers[1].kill() + ]) + + const config = { geo_ip: { enabled: true } } + await Promise.all([ + servers[0].run(config), + servers[1].run(config) + ]) + + await servers[0].views.view({ id: uuid, xForwardedFor: '8.8.8.8,127.0.0.1', currentTime: 1 }) + await servers[1].views.view({ id: uuid, xForwardedFor: '8.8.8.4,127.0.0.1', currentTime: 3 }) + await servers[1].views.view({ id: uuid, xForwardedFor: '80.67.169.12,127.0.0.1', currentTime: 2 }) + + await processViewersStats(servers) + + const stats = await servers[0].videoStats.getOverallStats({ videoId: uuid }) + expect(stats.countries).to.have.lengthOf(2) + + expect(stats.countries[0].isoCode).to.equal('US') + expect(stats.countries[0].viewers).to.equal(2) + + expect(stats.countries[1].isoCode).to.equal('FR') + expect(stats.countries[1].viewers).to.equal(1) + }) + }) + + after(async function () { + await cleanupTests(servers) + }) +}) diff --git a/server/tests/api/views/video-views-retention-stats.ts b/server/tests/api/views/video-views-retention-stats.ts new file mode 100644 index 000000000..98be7bfdb --- /dev/null +++ b/server/tests/api/views/video-views-retention-stats.ts @@ -0,0 +1,56 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import 'mocha' +import * as chai from 'chai' +import { prepareViewsServers, prepareViewsVideos, processViewersStats } from '@server/tests/shared' +import { cleanupTests, PeerTubeServer } from '@shared/server-commands' + +const expect = chai.expect + +describe('Test views retention stats', function () { + let servers: PeerTubeServer[] + + before(async function () { + this.timeout(120000) + + servers = await prepareViewsServers() + }) + + describe('Test retention stats on VOD', function () { + let vodVideoId: string + + before(async function () { + this.timeout(60000); + + ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true })) + }) + + it('Should display empty retention', async function () { + const { data } = await servers[0].videoStats.getRetentionStats({ videoId: vodVideoId }) + expect(data).to.have.lengthOf(6) + + for (let i = 0; i < 6; i++) { + expect(data[i].second).to.equal(i) + expect(data[i].retentionPercent).to.equal(0) + } + }) + + it('Should display appropriate retention metrics', async function () { + await servers[0].views.simulateViewer({ xForwardedFor: '127.0.0.2,127.0.0.1', id: vodVideoId, currentTimes: [ 0, 1 ] }) + await servers[0].views.simulateViewer({ xForwardedFor: '127.0.0.3,127.0.0.1', id: vodVideoId, currentTimes: [ 1, 3 ] }) + await servers[1].views.simulateViewer({ xForwardedFor: '127.0.0.2,127.0.0.1', id: vodVideoId, currentTimes: [ 4 ] }) + await servers[1].views.simulateViewer({ xForwardedFor: '127.0.0.3,127.0.0.1', id: vodVideoId, currentTimes: [ 0, 1 ] }) + + await processViewersStats(servers) + + const { data } = await servers[0].videoStats.getRetentionStats({ videoId: vodVideoId }) + expect(data).to.have.lengthOf(6) + + expect(data.map(d => d.retentionPercent)).to.deep.equal([ 50, 75, 25, 25, 25, 0 ]) + }) + }) + + after(async function () { + await cleanupTests(servers) + }) +}) diff --git a/server/tests/api/views/video-views-timeserie-stats.ts b/server/tests/api/views/video-views-timeserie-stats.ts new file mode 100644 index 000000000..98c041cdf --- /dev/null +++ b/server/tests/api/views/video-views-timeserie-stats.ts @@ -0,0 +1,109 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import 'mocha' +import * as chai from 'chai' +import { FfmpegCommand } from 'fluent-ffmpeg' +import { prepareViewsServers, prepareViewsVideos, processViewersStats } from '@server/tests/shared' +import { VideoStatsTimeserie, VideoStatsTimeserieMetric } from '@shared/models' +import { cleanupTests, PeerTubeServer, stopFfmpeg } from '@shared/server-commands' + +const expect = chai.expect + +describe('Test views timeserie stats', function () { + const availableMetrics: VideoStatsTimeserieMetric[] = [ 'viewers' ] + + let servers: PeerTubeServer[] + + before(async function () { + this.timeout(120000) + + servers = await prepareViewsServers() + }) + + describe('Common metric tests', function () { + let vodVideoId: string + + before(async function () { + this.timeout(60000); + + ({ vodVideoId } = await prepareViewsVideos({ servers, live: false, vod: true })) + }) + + it('Should display empty metric stats', async function () { + for (const metric of availableMetrics) { + const { data } = await servers[0].videoStats.getTimeserieStats({ videoId: vodVideoId, metric }) + + expect(data).to.have.lengthOf(30) + + for (const d of data) { + expect(d.value).to.equal(0) + } + } + }) + }) + + describe('Test viewer and watch time metrics on live and VOD', function () { + let vodVideoId: string + let liveVideoId: string + let command: FfmpegCommand + + function expectTimeserieData (result: VideoStatsTimeserie, lastValue: number) { + const { data } = result + expect(data).to.have.lengthOf(30) + + const last = data[data.length - 1] + + const today = new Date().getDate() + expect(new Date(last.date).getDate()).to.equal(today) + expect(last.value).to.equal(lastValue) + + for (let i = 0; i < data.length - 2; i++) { + expect(data[i].value).to.equal(0) + } + } + + before(async function () { + this.timeout(60000); + + ({ vodVideoId, liveVideoId, ffmpegCommand: command } = await prepareViewsVideos({ servers, live: true, vod: true })) + }) + + it('Should display appropriate viewers metrics', async function () { + for (const videoId of [ vodVideoId, liveVideoId ]) { + await servers[0].views.simulateViewer({ id: videoId, currentTimes: [ 0, 3 ] }) + await servers[1].views.simulateViewer({ id: videoId, currentTimes: [ 0, 5 ] }) + } + + await processViewersStats(servers) + + for (const videoId of [ vodVideoId, liveVideoId ]) { + const result = await servers[0].videoStats.getTimeserieStats({ videoId, metric: 'viewers' }) + expectTimeserieData(result, 2) + } + }) + + it('Should display appropriate watch time metrics', async function () { + for (const videoId of [ vodVideoId, liveVideoId ]) { + const result = await servers[0].videoStats.getTimeserieStats({ videoId, metric: 'aggregateWatchTime' }) + expectTimeserieData(result, 8) + + await servers[1].views.simulateViewer({ id: videoId, currentTimes: [ 0, 1 ] }) + } + + await processViewersStats(servers) + + for (const videoId of [ vodVideoId, liveVideoId ]) { + const result = await servers[0].videoStats.getTimeserieStats({ videoId, metric: 'aggregateWatchTime' }) + expectTimeserieData(result, 9) + } + }) + + after(async function () { + await stopFfmpeg(command) + }) + }) + + after(async function () { + await cleanupTests(servers) + }) +}) diff --git a/server/tests/api/views/videos-views-cleaner.ts b/server/tests/api/views/videos-views-cleaner.ts new file mode 100644 index 000000000..ef988837f --- /dev/null +++ b/server/tests/api/views/videos-views-cleaner.ts @@ -0,0 +1,101 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import 'mocha' +import * as chai from 'chai' +import { wait } from '@shared/core-utils' +import { + cleanupTests, + createMultipleServers, + doubleFollow, + killallServers, + PeerTubeServer, + setAccessTokensToServers, + waitJobs +} from '@shared/server-commands' + +const expect = chai.expect + +describe('Test video views cleaner', function () { + let servers: PeerTubeServer[] + + let videoIdServer1: string + let videoIdServer2: string + + before(async function () { + this.timeout(120000) + + servers = await createMultipleServers(2) + await setAccessTokensToServers(servers) + + await doubleFollow(servers[0], servers[1]) + + videoIdServer1 = (await servers[0].videos.quickUpload({ name: 'video server 1' })).uuid + videoIdServer2 = (await servers[1].videos.quickUpload({ name: 'video server 2' })).uuid + + await waitJobs(servers) + + await servers[0].views.simulateView({ id: videoIdServer1 }) + await servers[1].views.simulateView({ id: videoIdServer1 }) + await servers[0].views.simulateView({ id: videoIdServer2 }) + await servers[1].views.simulateView({ id: videoIdServer2 }) + + await waitJobs(servers) + }) + + it('Should not clean old video views', async function () { + this.timeout(50000) + + await killallServers([ servers[0] ]) + + await servers[0].run({ views: { videos: { remote: { max_age: '10 days' } } } }) + + await wait(6000) + + // Should still have views + + { + for (const server of servers) { + const total = await server.sql.countVideoViewsOf(videoIdServer1) + expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views') + } + } + + { + for (const server of servers) { + const total = await server.sql.countVideoViewsOf(videoIdServer2) + expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views') + } + } + }) + + it('Should clean old video views', async function () { + this.timeout(50000) + + await killallServers([ servers[0] ]) + + await servers[0].run({ views: { videos: { remote: { max_age: '5 seconds' } } } }) + + await wait(6000) + + // Should still have views + + { + for (const server of servers) { + const total = await server.sql.countVideoViewsOf(videoIdServer1) + expect(total).to.equal(2) + } + } + + { + const totalServer1 = await servers[0].sql.countVideoViewsOf(videoIdServer2) + expect(totalServer1).to.equal(0) + + const totalServer2 = await servers[1].sql.countVideoViewsOf(videoIdServer2) + expect(totalServer2).to.equal(2) + } + }) + + after(async function () { + await cleanupTests(servers) + }) +}) -- cgit v1.2.3