diff options
-rw-r--r-- | server/middlewares/cache/shared/api-cache.ts | 6 | ||||
-rw-r--r-- | server/tests/feeds/feeds.ts | 110 |
2 files changed, 93 insertions, 23 deletions
diff --git a/server/middlewares/cache/shared/api-cache.ts b/server/middlewares/cache/shared/api-cache.ts index c6197b972..b50b7dce4 100644 --- a/server/middlewares/cache/shared/api-cache.ts +++ b/server/middlewares/cache/shared/api-cache.ts | |||
@@ -35,7 +35,11 @@ export class ApiCache { | |||
35 | // Cache keys per group | 35 | // Cache keys per group |
36 | private groups: { [groupIndex: string]: string[] } = {} | 36 | private groups: { [groupIndex: string]: string[] } = {} |
37 | 37 | ||
38 | private readonly seed: number | ||
39 | |||
38 | constructor (options: APICacheOptions) { | 40 | constructor (options: APICacheOptions) { |
41 | this.seed = new Date().getTime() | ||
42 | |||
39 | this.options = { | 43 | this.options = { |
40 | headerBlacklist: [], | 44 | headerBlacklist: [], |
41 | excludeStatus: [], | 45 | excludeStatus: [], |
@@ -88,7 +92,7 @@ export class ApiCache { | |||
88 | } | 92 | } |
89 | 93 | ||
90 | private getCacheKey (req: express.Request) { | 94 | private getCacheKey (req: express.Request) { |
91 | return Redis.Instance.getPrefix() + 'api-cache-' + req.originalUrl | 95 | return Redis.Instance.getPrefix() + 'api-cache-' + this.seed + '-' + req.originalUrl |
92 | } | 96 | } |
93 | 97 | ||
94 | private shouldCacheResponse (response: express.Response) { | 98 | private shouldCacheResponse (response: express.Response) { |
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts index 286c03596..8433c873e 100644 --- a/server/tests/feeds/feeds.ts +++ b/server/tests/feeds/feeds.ts | |||
@@ -14,6 +14,7 @@ import { | |||
14 | PluginsCommand, | 14 | PluginsCommand, |
15 | setAccessTokensToServers, | 15 | setAccessTokensToServers, |
16 | setDefaultChannelAvatar, | 16 | setDefaultChannelAvatar, |
17 | setDefaultVideoChannel, | ||
17 | stopFfmpeg, | 18 | stopFfmpeg, |
18 | waitJobs | 19 | waitJobs |
19 | } from '@shared/server-commands' | 20 | } from '@shared/server-commands' |
@@ -53,6 +54,7 @@ describe('Test syndication feeds', () => { | |||
53 | 54 | ||
54 | await setAccessTokensToServers([ ...servers, serverHLSOnly ]) | 55 | await setAccessTokensToServers([ ...servers, serverHLSOnly ]) |
55 | await setDefaultChannelAvatar(servers[0]) | 56 | await setDefaultChannelAvatar(servers[0]) |
57 | await setDefaultVideoChannel(servers) | ||
56 | await doubleFollow(servers[0], servers[1]) | 58 | await doubleFollow(servers[0], servers[1]) |
57 | 59 | ||
58 | await servers[0].config.enableLive({ allowReplay: false, transcoding: false }) | 60 | await servers[0].config.enableLive({ allowReplay: false, transcoding: false }) |
@@ -137,28 +139,6 @@ describe('Test syndication feeds', () => { | |||
137 | }) | 139 | }) |
138 | }) | 140 | }) |
139 | 141 | ||
140 | it('Should serve the endpoint as a cached request', async function () { | ||
141 | const res = await makeGetRequest({ | ||
142 | url: servers[0].url, | ||
143 | path: '/feeds/videos.xml', | ||
144 | accept: 'application/xml', | ||
145 | expectedStatus: HttpStatusCode.OK_200 | ||
146 | }) | ||
147 | |||
148 | expect(res.headers['x-api-cache-cached']).to.equal('true') | ||
149 | }) | ||
150 | |||
151 | it('Should not serve the endpoint as a cached request', async function () { | ||
152 | const res = await makeGetRequest({ | ||
153 | url: servers[0].url, | ||
154 | path: '/feeds/videos.xml?v=186', | ||
155 | accept: 'application/xml', | ||
156 | expectedStatus: HttpStatusCode.OK_200 | ||
157 | }) | ||
158 | |||
159 | expect(res.headers['x-api-cache-cached']).to.not.exist | ||
160 | }) | ||
161 | |||
162 | it('Should refuse to serve the endpoint without accept header', async function () { | 142 | it('Should refuse to serve the endpoint without accept header', async function () { |
163 | await makeGetRequest({ url: servers[0].url, path: '/feeds/videos.xml', expectedStatus: HttpStatusCode.NOT_ACCEPTABLE_406 }) | 143 | await makeGetRequest({ url: servers[0].url, path: '/feeds/videos.xml', expectedStatus: HttpStatusCode.NOT_ACCEPTABLE_406 }) |
164 | }) | 144 | }) |
@@ -614,6 +594,92 @@ describe('Test syndication feeds', () => { | |||
614 | 594 | ||
615 | }) | 595 | }) |
616 | 596 | ||
597 | describe('Cache', function () { | ||
598 | const uuids: string[] = [] | ||
599 | |||
600 | function doPodcastRequest () { | ||
601 | return makeGetRequest({ | ||
602 | url: servers[0].url, | ||
603 | path: '/feeds/podcast/videos.xml', | ||
604 | query: { videoChannelId: servers[0].store.channel.id }, | ||
605 | accept: 'application/xml', | ||
606 | expectedStatus: HttpStatusCode.OK_200 | ||
607 | }) | ||
608 | } | ||
609 | |||
610 | function doVideosRequest (query: { [id: string]: string } = {}) { | ||
611 | return makeGetRequest({ | ||
612 | url: servers[0].url, | ||
613 | path: '/feeds/videos.xml', | ||
614 | query, | ||
615 | accept: 'application/xml', | ||
616 | expectedStatus: HttpStatusCode.OK_200 | ||
617 | }) | ||
618 | } | ||
619 | |||
620 | before(async function () { | ||
621 | { | ||
622 | const { uuid } = await servers[0].videos.quickUpload({ name: 'cache 1' }) | ||
623 | uuids.push(uuid) | ||
624 | } | ||
625 | |||
626 | { | ||
627 | const { uuid } = await servers[0].videos.quickUpload({ name: 'cache 2' }) | ||
628 | uuids.push(uuid) | ||
629 | } | ||
630 | }) | ||
631 | |||
632 | it('Should serve the videos endpoint as a cached request', async function () { | ||
633 | await doVideosRequest() | ||
634 | |||
635 | const res = await doVideosRequest() | ||
636 | |||
637 | expect(res.headers['x-api-cache-cached']).to.equal('true') | ||
638 | }) | ||
639 | |||
640 | it('Should not serve the videos endpoint as a cached request', async function () { | ||
641 | const res = await doVideosRequest({ v: '186' }) | ||
642 | |||
643 | expect(res.headers['x-api-cache-cached']).to.not.exist | ||
644 | }) | ||
645 | |||
646 | it('Should invalidate the podcast feed cache after video deletion', async function () { | ||
647 | await doPodcastRequest() | ||
648 | |||
649 | { | ||
650 | const res = await doPodcastRequest() | ||
651 | expect(res.headers['x-api-cache-cached']).to.exist | ||
652 | } | ||
653 | |||
654 | await servers[0].videos.remove({ id: uuids[0] }) | ||
655 | |||
656 | { | ||
657 | const res = await doPodcastRequest() | ||
658 | expect(res.headers['x-api-cache-cached']).to.not.exist | ||
659 | } | ||
660 | }) | ||
661 | |||
662 | it('Should invalidate the podcast feed cache after video deletion, even after server restart', async function () { | ||
663 | this.timeout(120000) | ||
664 | |||
665 | await doPodcastRequest() | ||
666 | |||
667 | { | ||
668 | const res = await doPodcastRequest() | ||
669 | expect(res.headers['x-api-cache-cached']).to.exist | ||
670 | } | ||
671 | |||
672 | await servers[0].kill() | ||
673 | await servers[0].run() | ||
674 | |||
675 | await servers[0].videos.remove({ id: uuids[1] }) | ||
676 | |||
677 | const res = await doPodcastRequest() | ||
678 | expect(res.headers['x-api-cache-cached']).to.not.exist | ||
679 | }) | ||
680 | |||
681 | }) | ||
682 | |||
617 | after(async function () { | 683 | after(async function () { |
618 | await servers[0].plugins.uninstall({ npmName: 'peertube-plugin-test-podcast-custom-tags' }) | 684 | await servers[0].plugins.uninstall({ npmName: 'peertube-plugin-test-podcast-custom-tags' }) |
619 | 685 | ||