import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { describe, expect, it } from 'vitest'; import type { SiteConfig } from '@/content/types'; import { parseHashedArtifactManifest, renderNginxConfig, // @ts-expect-error The executable ESM build script intentionally has no publishable declaration file. } from '../../scripts/generate-host-config.mjs'; const jsonLdHash = 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='; const releaseManifestDigest = 'd'.repeat(64); const productionSite: SiteConfig = { environment: 'production', canonicalUrl: 'https://portfolio.example.test/', buildId: 'release-1', }; const previewSite: SiteConfig = { environment: 'preview', canonicalUrl: 'https://portfolio.localhost/', buildId: 'preview-1', }; const manifest = { schema: 'portfolio-host-artifact-manifest/v1', files: [ { path: 'assets/app-a1b2c3d4.css', sha256: 'b'.repeat(64) }, { path: 'assets/app-a1b2c3d4.js', sha256: 'a'.repeat(64) }, { path: 'assets/demo-c3d4e5f6.webm', sha256: 'c'.repeat(64) }, ], }; describe('generated Nginx static-host contract', () => { it('binds immutable caching to the exact hashed artifact manifest', () => { const config = renderNginxConfig({ site: productionSite, jsonLdCspHash: jsonLdHash, artifactManifest: manifest, documentRoot: '/srv/portfolio/current', alternateHosts: ['www.portfolio.example.test'], tlsCertificatePath: '/etc/portfolio/tls/fullchain.pem', tlsKeyPath: '/etc/portfolio/tls/privkey.pem', manifestDigest: releaseManifestDigest, }); for (const file of manifest.files) expect(config).toContain(`~^/${file.path.replaceAll('.', '\\.')}$`); expect(config).toContain('public, max-age=31536000, immutable'); expect(config).toContain('public, max-age=0, must-revalidate'); expect(config).toContain( 'Requires standard MIME mappings and static-file defaults in the parent Nginx http context.', ); expect(config).not.toContain('include /etc/nginx/mime.types;'); expect(config).not.toContain('default_type application/octet-stream;'); expect(config).not.toContain('sendfile on;'); expect(config).toContain('max_ranges 1;'); expect(config).not.toContain('max_ranges 0'); }); it('redirects HTTP and an alternate HTTPS host directly to the canonical HTTPS host', () => { const config = renderNginxConfig({ site: productionSite, jsonLdCspHash: jsonLdHash, artifactManifest: manifest, documentRoot: '/srv/portfolio/current', alternateHosts: ['www.portfolio.example.test'], tlsCertificatePath: '/etc/portfolio/tls/fullchain.pem', tlsKeyPath: '/etc/portfolio/tls/privkey.pem', manifestDigest: releaseManifestDigest, }); expect(config).toContain('listen 80;'); expect(config).toContain('server_name portfolio.example.test www.portfolio.example.test;'); expect(config.match(/return 308 https:\/\/portfolio\.example\.test\$request_uri;/gu)).toHaveLength(2); expect(config).not.toMatch(/return 30[127] /u); expect(config).not.toContain('http://portfolio.example.test'); }); it('applies exact browser headers and production indexability without public source maps', () => { const config = renderNginxConfig({ site: productionSite, jsonLdCspHash: jsonLdHash, artifactManifest: manifest, documentRoot: '/srv/portfolio/current', alternateHosts: [], tlsCertificatePath: '/etc/portfolio/tls/fullchain.pem', tlsKeyPath: '/etc/portfolio/tls/privkey.pem', manifestDigest: releaseManifestDigest, }); expect(config).toContain("default-src 'self'; script-src 'self' 'sha256-"); expect(config).toContain("img-src 'self' data:"); expect(config).toContain("connect-src 'self'"); expect(config).toContain('add_header Strict-Transport-Security "max-age=31536000" always;'); expect(config).toContain(`add_header X-Portfolio-Manifest-Digest "${releaseManifestDigest}" always;`); expect(config).toContain('gzip on;'); expect(config).toContain('gzip_min_length 1;'); expect(config).toContain('gzip_vary on;'); expect(config).toContain('gzip_types text/css application/javascript application/json image/svg+xml;'); expect(config).not.toContain('includeSubDomains'); expect(config).not.toContain('X-Robots-Tag'); expect(config).not.toMatch(/unsafe-inline|unsafe-eval|sourceMappingURL|\.map(?:\s|$)/u); }); it('makes preview responses private and noindex without a sitemap or HSTS', () => { const config = renderNginxConfig({ site: previewSite, jsonLdCspHash: jsonLdHash, artifactManifest: manifest, documentRoot: '/srv/portfolio/preview', alternateHosts: [], }); expect(config).toContain('add_header X-Robots-Tag "noindex, nofollow, noarchive" always;'); expect(config).toContain('private, no-store'); expect(config).not.toContain('Strict-Transport-Security'); expect(config).not.toContain('sitemap.xml'); expect(config).not.toContain('X-Portfolio-Manifest-Digest'); }); it('rejects production configuration without the signed manifest digest', () => { expect(() => renderNginxConfig({ site: productionSite, jsonLdCspHash: jsonLdHash, artifactManifest: manifest, documentRoot: '/srv/portfolio/current', alternateHosts: [], tlsCertificatePath: '/etc/portfolio/tls/fullchain.pem', tlsKeyPath: '/etc/portfolio/tls/privkey.pem', })).toThrow(/manifest digest/i); }); it.each([ { name: 'a source map', value: { ...manifest, files: [{ path: 'assets/app-a1b2c3d4.js.map', sha256: 'a'.repeat(64) }], }, }, { name: 'an external or unsafe path', value: { ...manifest, files: [{ path: '../app-a1b2c3d4.js', sha256: 'a'.repeat(64) }], }, }, { name: 'a non-canonical digest', value: { ...manifest, files: [{ path: 'assets/app-a1b2c3d4.js', sha256: 'A'.repeat(64) }], }, }, ])('rejects $name in the hashed artifact manifest', ({ value }) => { expect(() => parseHashedArtifactManifest(value)).toThrow(/artifact manifest/i); }); it('rejects document-root values that cannot be emitted as one Nginx token', () => { expect(() => renderNginxConfig({ site: previewSite, jsonLdCspHash: jsonLdHash, artifactManifest: manifest, documentRoot: '/srv/portfolio/injected root', alternateHosts: [], }), ).toThrow(/document root/i); }); it('ships a domain-neutral example instead of inventing Luca’s production hostname', () => { const example = readFileSync(resolve(process.cwd(), 'nginx.portfolio.conf.example'), 'utf8'); expect(example).toContain('__CANONICAL_HOST__'); expect(example).toContain('__JSON_LD_CSP_HASH__'); expect(example).toContain('__MANIFEST_DIGEST__'); expect(example).not.toContain('portfolio.example.test'); expect(example).not.toMatch(/server_name\s+[a-z0-9.-]+\.[a-z]{2,}/iu); }); });