import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs/promises'; test('Caddy caches only content-hashed Vite frontend assets as immutable', async () => { const caddyfile = await fs.readFile(new URL('../caddy/Caddyfile', import.meta.url), 'utf8'); const matcher = caddyfile.match( /@immutableFrontendAssets path_regexp immutableFrontendAssets (\S+)/, ); assert.ok(matcher, 'immutable frontend asset matcher must exist'); const assetPath = new RegExp(matcher[1]); for (const profile of ['gateway', 'che', 'kwe', 'pwe', 'twe', 'nya', 'pya', 'hwe']) { assert.equal(assetPath.test(`/${profile}/assets/index-CcXtLSVk.js`), true); assert.equal(assetPath.test(`/${profile}/assets/index-CcXtLSVk.js.map`), true); } assert.equal(assetPath.test('/gateway/'), false); assert.equal(assetPath.test('/gateway/terms.1.html'), false); assert.equal(assetPath.test('/gateway/assets/index.js'), false); assert.equal(assetPath.test('/image/game/figure.png'), false); assert.match( caddyfile, /header @immutableFrontendAssets >Cache-Control "public, max-age=31536000, immutable"/, ); const sharedMatcher = caddyfile.match( /@immutableSharedProfileAssets path_regexp immutableSharedProfileAssets (\S+)/, ); assert.ok(sharedMatcher, 'immutable shared profile asset matcher must exist'); const sharedAssetPath = new RegExp(sharedMatcher[1]); const releaseId = `${'a'.repeat(40)}-${'b'.repeat(16)}`; assert.equal( sharedAssetPath.test(`/gateway/profile-assets/${releaseId}/assets/index-CcXtLSVk.js`), true, ); assert.equal( sharedAssetPath.test(`/gateway/profile-assets/${releaseId}/assets/index-CcXtLSVk.js.map`), true, ); assert.equal(sharedAssetPath.test(`/gateway/profile-assets/${releaseId}/index.html`), false); assert.match( caddyfile, /header @immutableSharedProfileAssets >Cache-Control "public, max-age=31536000, immutable"/, ); }); test('Caddy serves published frontend artifacts directly and keeps a transition fallback', async () => { const caddyfile = await fs.readFile(new URL('../caddy/Caddyfile', import.meta.url), 'utf8'); for (const [profile, port] of Object.entries({ gateway: 15000, che: 15002, kwe: 15004, pwe: 15006, twe: 15008, nya: 15010, pya: 15012, hwe: 15014, })) { assert.match(caddyfile, new RegExp(`/srv/frontend-artifacts/${profile}/current`)); assert.match(caddyfile, new RegExp(`reverse_proxy runtime:${port}`)); assert.match( caddyfile, new RegExp(`route \\{[\\s\\S]*?@${profile}Asset path /assets/\\*[\\s\\S]*?handle @${profile}Asset`), ); } assert.match(caddyfile, /try_files \{path\} \/index\.html/); assert.match(caddyfile, /header @frontendRequests Cache-Control "no-cache"/); assert.match( caddyfile, /@sharedProfileAsset path_regexp sharedProfileAsset \^\/gateway\/profile-assets\//, ); assert.match(caddyfile, /root \* \/srv\/frontend-artifacts\/game-assets\/releases/); assert.match(caddyfile, /uri strip_prefix \/gateway\/profile-assets/); });