import { describe, expect, it, vi } from 'vitest'; import type { PostData } from '../../src/main/engine/PostEngine'; import { createGenerationRouteRenderer, createPreviewBackedGenerationRouteRenderer, } from '../../src/main/engine/GenerationRouteRendererFactory'; function makePost(overrides: Partial = {}): PostData { const createdAt = overrides.createdAt ?? new Date('2025-01-15T10:00:00.000Z'); return { id: overrides.id ?? 'post-1', projectId: overrides.projectId ?? 'project', title: overrides.title ?? 'Title', slug: overrides.slug ?? 'title', excerpt: overrides.excerpt, content: overrides.content ?? 'Body', status: overrides.status ?? 'published', author: overrides.author, createdAt, updatedAt: overrides.updatedAt ?? createdAt, publishedAt: overrides.publishedAt ?? createdAt, tags: overrides.tags ?? [], categories: overrides.categories ?? [], }; } describe('GenerationRouteRendererFactory', () => { it('normalizes route keys and memoizes html rendering calls', async () => { const renderWithContext = vi.fn().mockResolvedValue('ok'); const renderRoute = createGenerationRouteRenderer({ renderWithContext, context: { projectContext: { projectId: 'p', dataDir: '/tmp', projectName: 'P', projectDescription: 'D', }, metadata: { name: 'P', description: 'D', }, menu: { items: [] }, maxPostsPerPage: 50, }, }); const a = await renderRoute('/foo/'); const b = await renderRoute('/foo'); expect(a).toBe('ok'); expect(b).toBe('ok'); expect(renderWithContext).toHaveBeenCalledTimes(1); expect(renderWithContext).toHaveBeenCalledWith('/foo', expect.any(Object)); }); it('keeps day archive query caches distinct across different dates', async () => { const posts = [ makePost({ id: 'day-1', slug: 'day-1', title: 'Post On Day One', createdAt: new Date('2025-01-15T10:00:00.000Z'), }), makePost({ id: 'day-2', slug: 'day-2', title: 'Post On Day Two', createdAt: new Date('2025-01-16T10:00:00.000Z'), }), ]; const postEngine = { getPostsFiltered: vi.fn(async (filter: { status?: 'draft' | 'published' | 'archived'; startDate?: Date; endDate?: Date; year?: number; month?: number; }) => { let filtered = posts.filter((post) => post.status === (filter.status ?? post.status)); if (typeof filter.year === 'number') { filtered = filtered.filter((post) => post.createdAt.getFullYear() === filter.year); } if (typeof filter.month === 'number') { filtered = filtered.filter((post) => post.createdAt.getMonth() === filter.month); } if (filter.startDate) { filtered = filtered.filter((post) => post.createdAt >= filter.startDate as Date); } if (filter.endDate) { filtered = filtered.filter((post) => post.createdAt <= filter.endDate as Date); } return filtered; }), getPublishedVersion: vi.fn(async () => null), findPublishedBySlug: vi.fn(async (slug: string) => posts.find((post) => post.slug === slug) ?? null), getPost: vi.fn(async (id: string) => posts.find((post) => post.id === id) ?? null), hasPublishedVersion: vi.fn(async () => false), setProjectContext: vi.fn(), }; const renderRoute = createPreviewBackedGenerationRouteRenderer({ options: { projectId: 'project', dataDir: '/tmp', projectName: 'Project', }, maxPostsPerPage: 50, publishedPostsForLookup: posts, engines: { postEngine, mediaEngine: { getAllMedia: vi.fn(async () => []), setProjectContext: vi.fn(), }, postMediaEngine: { setProjectContext: vi.fn(), getLinkedMediaForPost: vi.fn(async () => []), getLinkedMediaDataForPost: vi.fn(async () => []), }, }, }); const dayOneHtml = await renderRoute('/2025/01/15'); const dayTwoHtml = await renderRoute('/2025/01/16'); expect(dayOneHtml).toContain('Post On Day One'); expect(dayOneHtml).not.toContain('Post On Day Two'); expect(dayTwoHtml).toContain('Post On Day Two'); expect(dayTwoHtml).not.toContain('Post On Day One'); }); });