import { readFileSync } from 'node:fs' import { join } from 'node:path' import { describe, expect, it } from 'vitest' // The CloudFront Functions runtime wants a bare top-level handler, so the // source has no exports; evaluate it and pull the handler out. const source = readFileSync(join(import.meta.dirname, 'viewer-request.js'), 'utf8') const handler = new Function(`${source}; return handler;`)() function run(uri, querystring = {}) { return handler({ request: { uri, querystring } }) } describe('viewer-request.js', () => { it('leaves the root to default_root_object', () => { expect(run('/').uri).toBe('/') }) it('rewrites extensionless URIs to the index.html object', () => { expect(run('/posts').uri).toBe('/posts/index.html') expect(run('/posts/hello-world').uri).toBe('/posts/hello-world/index.html') }) it('passes file URIs through untouched', () => { expect(run('/atom.xml').uri).toBe('/atom.xml') expect(run('/client-metadata.json').uri).toBe('/client-metadata.json') expect(run('/_astro/index.abc123.css').uri).toBe('/_astro/index.abc123.css') }) it('redirects trailing slashes to the canonical no-slash address', () => { const response = run('/posts/hello-world/') expect(response.statusCode).toBe(301) expect(response.headers.location.value).toBe('/posts/hello-world') }) it('collapses runs of trailing slashes without redirect-looping the root', () => { expect(run('/posts///').headers.location.value).toBe('/posts') expect(run('///').headers.location.value).toBe('/') }) it('keeps the query string on redirects', () => { const querystring = { ref: { value: 'feed' }, flag: { value: '' }, tag: { value: 'a', multiValue: [{ value: 'a' }, { value: 'b' }] }, } const response = run('/posts/hello-world/', querystring) expect(response.headers.location.value).toBe( '/posts/hello-world?ref=feed&flag&tag=a&tag=b', ) }) })