import assert from 'node:assert/strict'; import { rm } from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { after, before, test } from 'node:test'; import { createApp } from '../src/app.js'; import { loadConfig } from '../src/config.js'; import { createContentStore } from '../src/content.js'; import { createFixture } from './fixtures.js'; let dir; let server; let base; before(async () => { dir = await createFixture(); const config = loadConfig({ production: true, showDrafts: false, contentDir: dir, cacheDir: path.join(os.tmpdir(), `site-cache-${process.pid}`), siteUrl: 'https://example.org', }); const content = createContentStore(config); const app = await createApp({ config, content }); server = app.listen(0); await new Promise((resolve) => server.once('listening', resolve)); base = `http://127.0.0.1:${server.address().port}`; }); after(async () => { server.close(); await rm(dir, { recursive: true, force: true }); await rm(path.join(os.tmpdir(), `site-cache-${process.pid}`), { recursive: true, force: true }); }); const get = (url, options) => fetch(base + url, { redirect: 'manual', ...options }); test('pages render in both languages', async () => { for (const url of [ '/de/', '/en/', '/de/portfolio', '/en/portfolio', '/de/portfolio/alpha', '/en/portfolio/alpha', '/de/werdegang', '/en/journey', '/de/kontakt', '/en/contact', ]) { const res = await get(url); assert.equal(res.status, 200, url); assert.match(res.headers.get('content-security-policy'), /script-src 'self'/); } }); test('the root redirects by Accept-Language', async () => { let res = await get('/', { headers: { 'Accept-Language': 'en-GB,en;q=0.9' } }); assert.equal(res.status, 302); assert.equal(res.headers.get('location'), '/en/'); res = await get('/', { headers: { 'Accept-Language': 'fr' } }); assert.equal(res.headers.get('location'), '/de/'); }); test('drafts, hidden pages and wrong slugs are 404', async () => { for (const url of ['/de/portfolio/beta', '/de/homelab', '/de/journey', '/en/werdegang', '/de/nope', '/xx']) { const res = await get(url); assert.equal(res.status, 404, url); } }); test('homepage hides links to pages that do not exist yet', async () => { const html = await (await get('/de/')).text(); assert.doesNotMatch(html, /href="\/de\/homelab"/); assert.match(html, /

{ const html = await (await get('/de/portfolio?tag=nothing')).text(); assert.doesNotMatch(html, /data-tags="web" hidden/); }); test('media: resized images, no traversal, no source files', async () => { let res = await get('/media/projects/alpha/cover.png?w=480'); assert.equal(res.status, 200); assert.equal(res.headers.get('content-type'), 'image/webp'); res = await get('/media/projects/alpha/cover.png?w=480&v=abc'); assert.match(res.headers.get('cache-control'), /immutable/); res = await get('/media/projects/alpha/cover.png?w=123'); assert.equal(res.status, 404); for (const url of ['/media/site.yml', '/media/projects/alpha/de.md', '/media/..%2Fpackage.json', '/media/projects/%2e%2e/%2e%2e/site.yml']) { res = await get(url); assert.equal(res.status, 404, url); } }); test('sitemap and robots', async () => { const sitemap = await (await get('/sitemap.xml')).text(); assert.match(sitemap, /https:\/\/example\.org\/en\/portfolio\/alpha<\/loc>/); assert.doesNotMatch(sitemap, /beta|homelab/); assert.match(await (await get('/robots.txt')).text(), /Sitemap: https:\/\/example\.org\/sitemap\.xml/); }); test('contact page links to the email address and has no form', async () => { const html = await (await get('/de/kontakt')).text(); assert.match(html, /href="mailto:test@example\.org"/); assert.doesNotMatch(html, /