import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { GET } from '../docs-page.js';
const INDEX_HTML = `
old metadata
`;
const AGENTS_MARKDOWN = '# gitlawb for AI agents\n\nEvery command verified.\n';
const BROWSER_ACCEPT =
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
beforeEach(() => {
vi.stubEnv('PUBLIC_SITE_URL', '');
});
/**
* Serves index.html and public/docs/*.md the way the deployment does.
*
* The important detail is that **nothing 404s**. vercel.json rewrites
* `/docs/:slug` into this function — and `:slug` matches a segment containing
* a dot — while `/(.*)` catches everything else and serves index.html. A
* missing `public/docs/.md` therefore does not produce a 404: it is
* rewritten back into the handler. An earlier version of this stub returned
* 404 for absent files, which is why a real bug went unnoticed — the handler
* served the hub index as though it were the requested document.
*/
function stubStaticFetch(present: readonly string[] = ['agents']): ReturnType {
const fetchMock = vi.fn().mockImplementation(async (input: URL | RequestInfo, init?: RequestInit) => {
const url = String(input instanceof Request ? input.url : input);
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
const docMatch = url.match(/\/docs\/([^/]+)\.md$/);
if (docMatch) {
if (present.includes(docMatch[1])) {
return new Response(AGENTS_MARKDOWN, {
status: 200,
headers: { 'Content-Type': 'text/markdown' },
});
}
// Filesystem miss → vercel.json rewrites `/docs/.md` back into
// this same function with slug=".md".
const rewritten = new Request(
`https://preview.example/api/docs-page?${new URLSearchParams({ slug: `${docMatch[1]}.md` })}`,
{ headers },
);
return GET(rewritten);
}
return new Response(INDEX_HTML, { status: 200, headers: { 'Content-Type': 'text/html' } });
});
vi.stubGlobal('fetch', fetchMock);
return fetchMock;
}
function docsRequest(slug?: string, headers: Record = { accept: BROWSER_ACCEPT }): Request {
const search = slug === undefined ? '' : `?${new URLSearchParams({ slug })}`;
return new Request(`https://preview.example/api/docs-page${search}`, { headers });
}
afterEach(() => {
vi.clearAllMocks();
vi.unstubAllGlobals();
vi.unstubAllEnvs();
});
describe('docs page HTML shell', () => {
it('injects per-section metadata with the docs OG card', async () => {
const staticFetch = stubStaticFetch();
const response = await GET(docsRequest('agents'));
const html = await response.text();
expect(response.status).toBe(200);
expect(response.headers.get('cache-control')).toBe(
'public, max-age=0, s-maxage=3600, stale-while-revalidate=86400',
);
expect(response.headers.get('vary')).toBe('Accept, User-Agent');
expect(staticFetch).toHaveBeenCalledTimes(1);
expect(html).toContain('for AI agents · docs · Twigpine Explorer');
expect(html).toContain('content="https://preview.example/og-docs.png"');
expect(html).toContain('href="https://preview.example/docs/agents"');
expect(html).not.toContain('old metadata');
expect(html).toContain('');
expect(html).toContain('');
});
it('serves hub metadata for the bare /docs route', async () => {
stubStaticFetch();
const response = await GET(docsRequest());
const html = await response.text();
expect(response.status).toBe(200);
expect(html).toContain('docs · Twigpine Explorer');
expect(html).toContain('href="https://preview.example/docs"');
expect(html).toContain('content="https://preview.example/og-docs.png"');
});
it('falls back to hub metadata for an unknown slug without echoing it', async () => {
stubStaticFetch();
const response = await GET(docsRequest('">'));
const html = await response.text();
expect(response.status).toBe(200);
expect(html).toContain('docs · Twigpine Explorer');
expect(html).toContain('href="https://preview.example/docs"');
expect(html).not.toContain('');
});
it('honors PUBLIC_SITE_URL for canonical and image URLs', async () => {
vi.stubEnv('PUBLIC_SITE_URL', 'https://explorer.twigpine.com');
stubStaticFetch();
const response = await GET(docsRequest('protocol'));
const html = await response.text();
expect(html).toContain('href="https://explorer.twigpine.com/docs/protocol"');
expect(html).toContain('content="https://explorer.twigpine.com/og-docs.png"');
});
it('serves the HTML shell to social crawlers that accept */*', async () => {
stubStaticFetch();
const response = await GET(docsRequest('agents', {
accept: '*/*',
'user-agent': 'Twitterbot/1.0',
}));
const html = await response.text();
expect(response.status).toBe(200);
expect(response.headers.get('content-type')).toContain('text/html');
expect(html).toContain('for AI agents · docs · Twigpine Explorer');
expect(html).toContain('');
});
it('returns a plain 502 when the shell cannot be fetched', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response('nope', { status: 500 })));
const response = await GET(docsRequest('agents'));
expect(response.status).toBe(502);
expect(response.headers.get('cache-control')).toBe('private, no-store, max-age=0');
expect(await response.text()).toContain('temporarily unavailable');
});
});
describe('docs page markdown negotiation', () => {
it('serves the raw section markdown to text clients', async () => {
const staticFetch = stubStaticFetch();
const response = await GET(docsRequest('agents', { accept: '*/*', 'user-agent': 'curl/8.7.1' }));
const body = await response.text();
expect(response.status).toBe(200);
expect(response.headers.get('content-type')).toBe('text/markdown; charset=utf-8');
expect(response.headers.get('vary')).toBe('Accept, User-Agent');
expect(response.headers.get('cache-control')).toBe(
'public, max-age=0, s-maxage=3600, stale-while-revalidate=86400',
);
expect(body).toBe(AGENTS_MARKDOWN);
expect(String(staticFetch.mock.calls[0][0])).toContain('/docs/agents.md');
});
it('serves a markdown index for the bare /docs route', async () => {
stubStaticFetch();
const response = await GET(docsRequest(undefined, { accept: '*/*' }));
const body = await response.text();
expect(response.status).toBe(200);
expect(response.headers.get('content-type')).toBe('text/markdown; charset=utf-8');
expect(body).toContain('# Twigpine docs');
expect(body).toContain('https://preview.example/docs/quickstart.md');
expect(body).toContain('https://preview.example/docs/agents.md');
expect(body).toContain('https://preview.example/skill.md');
expect(body).toContain('https://preview.example/llms.txt');
expect(body).not.toContain('');
});
it('serves the markdown index for unknown slugs without echoing them', async () => {
stubStaticFetch();
const response = await GET(docsRequest('nope">