import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from '@jest/globals' import mockfs from 'mock-fs' import request from 'supertest' import { createApp } from '../src/createApp' import { createMockServer, generateSimpleViteManifest, getRandomPort } from './util.js' describe('File caching service', () => { let app let mockserver const port = getRandomPort() beforeAll(() => { mockfs({ './config/manifests': { 'urls.yaml': `manifests: - http://localhost:${port}/manifest.json` } }) app = createApp() }) afterAll(() => { mockfs.restore() }) beforeEach(async () => { mockserver = await createMockServer({ port }) mockserver.respondWith({ '/manifest.json': generateSimpleViteManifest({ 'example.js': { imports: ['test.txt'] }, 'test.txt': { }, 'main.css': {}, 'index.html': { file: 'index.html.js', isEntry: true, imports: ['example.js'], css: ['main.css'] } }), '/example.js': (req, res) => res.setHeader('content-type', 'application/javascript').status(200).send('this is example'), '/test.txt': (req, res) => res.setHeader('content-type', 'text/plain').status(200).send('this is test'), '/index.html.js': (req, res) => res.setHeader('content-type', 'application/javascript').status(200).send('this is index.html.js'), '/index.html': (req, res) => res.setHeader('content-type', 'text/html').status(200).send('<html><head></head><body>it\'s me</body></html>'), '/main.css': (req, res) => res.setHeader('content-type', 'text/css').status(200).send('.foo { color: #000; }'), '/favicon.ico': 'not really a favicon, though' }) await request(app).get('/ready') }) afterEach(() => { mockserver.close() process.env.CACHE_TTL = 30000 }) it('serves files defined in manifest.json file', async () => { const response = await request(app).get('/example.js') expect(response.statusCode).toBe(200) expect(response.headers['content-type']).toBe('application/javascript; charset=utf-8') expect(response.text).toBe('this is example') // expect(response.headers['content-security-policy']).toContain('sha256-NzZhMTE2Njc2YTgyNTZmZTdlZGVjZDU3YTNmYzRjNmM1OWZkMTI2NjRkYzZmMWM3YTkwMGU3ZTdhNDlhZmVlMwo=') const response2 = await request(app).get('/test.txt') expect(response2.statusCode).toBe(200) expect(response2.headers['content-type']).toBe('text/plain; charset=utf-8') expect(response2.text).toBe('this is test') }) it('serves css files', async () => { const response = await request(app).get('/main.css') expect(response.statusCode).toBe(200) expect(response.headers['content-type']).toBe('text/css; charset=utf-8') // expect(response.headers['content-security-policy']).toContain('sha256-YjRiYWRlYTVhYmM5ZTZkNjE2ZGM4YjcwZWRlNzUxMmU0YjgxY2UxMWExOTI2ZjM1NzM1M2Y2MWJjNmUwMmZjMwo=') }) it('serves / as index.html', async () => { const response = await request(app).get('/') expect(response.statusCode).toBe(200) expect(response.headers['content-type']).toBe('text/html; charset=utf-8') expect(response.text).toBe('<html><head></head><body>it\'s me</body></html>') }) it('directly fetches files not referenced in manifest.json files from the upstream servers', async () => { const response = await request(app).get('/favicon.ico') expect(response.statusCode).toBe(200) expect(response.body).toBe('not really a favicon, though') }) it('returns 404 if file can not be resolved', async () => { const response = await request(app).get('/unknown-file.txt') expect(response.statusCode).toBe(404) }) })