|
| 1 | +/** |
| 2 | + * @vitest-environment node |
| 3 | + */ |
| 4 | +import fs from 'fs/promises' |
| 5 | +import os from 'os' |
| 6 | +import path from 'path' |
| 7 | +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' |
| 8 | + |
| 9 | +/** |
| 10 | + * `sharp` resolves a platform-specific `@img/sharp-*` native binary that the |
| 11 | + * standalone file tracer cannot follow, so a deployment can ship without it. It |
| 12 | + * must therefore be loaded lazily and its failure contained: an unreadable OG |
| 13 | + * dimension is optional metadata, not a reason to take down `/blog`, `/library`, |
| 14 | + * and every tag, author, slug, and RSS route that reads the registry. |
| 15 | + * |
| 16 | + * This mock makes `import('sharp')` fail the way a missing native binary does. |
| 17 | + */ |
| 18 | +vi.mock('sharp', () => { |
| 19 | + throw new Error('Could not load the sharp module using the linux-x64 runtime') |
| 20 | +}) |
| 21 | + |
| 22 | +vi.mock('next-mdx-remote/rsc', () => ({ |
| 23 | + compileMDX: vi.fn(async () => ({ content: null })), |
| 24 | +})) |
| 25 | + |
| 26 | +vi.mock('@/lib/content/mdx', () => ({ mdxComponents: {} })) |
| 27 | + |
| 28 | +import { createContentRegistry } from '@/lib/content/registry-factory' |
| 29 | + |
| 30 | +let root: string |
| 31 | +let contentDir: string |
| 32 | +let authorsDir: string |
| 33 | + |
| 34 | +const POST = `--- |
| 35 | +slug: sharp-is-unavailable |
| 36 | +title: Sharp Is Unavailable |
| 37 | +description: The registry still serves posts when the native binary is missing. |
| 38 | +date: 2026-08-10 |
| 39 | +authors: [waleed] |
| 40 | +ogImage: /blog/missing-og.png |
| 41 | +canonical: https://sim.ai/blog/sharp-is-unavailable |
| 42 | +--- |
| 43 | +
|
| 44 | +Body copy. |
| 45 | +` |
| 46 | + |
| 47 | +const AUTHOR = JSON.stringify({ id: 'waleed', name: 'Waleed Latif' }) |
| 48 | + |
| 49 | +beforeAll(async () => { |
| 50 | + root = await fs.mkdtemp(path.join(os.tmpdir(), 'content-registry-')) |
| 51 | + contentDir = path.join(root, 'content', 'blog') |
| 52 | + authorsDir = path.join(root, 'content', 'authors') |
| 53 | + await fs.mkdir(path.join(contentDir, 'sharp-is-unavailable'), { recursive: true }) |
| 54 | + await fs.mkdir(authorsDir, { recursive: true }) |
| 55 | + await fs.writeFile(path.join(contentDir, 'sharp-is-unavailable', 'index.mdx'), POST) |
| 56 | + await fs.writeFile(path.join(authorsDir, 'waleed.json'), AUTHOR) |
| 57 | +}) |
| 58 | + |
| 59 | +afterAll(async () => { |
| 60 | + await fs.rm(root, { recursive: true, force: true }) |
| 61 | +}) |
| 62 | + |
| 63 | +describe('createContentRegistry without a loadable sharp', () => { |
| 64 | + it('still lists posts, omitting only the OG dimensions', async () => { |
| 65 | + const registry = createContentRegistry({ contentDir, authorsDir }) |
| 66 | + |
| 67 | + const posts = await registry.getAllPostMeta() |
| 68 | + |
| 69 | + expect(posts).toHaveLength(1) |
| 70 | + expect(posts[0].slug).toBe('sharp-is-unavailable') |
| 71 | + expect(posts[0].ogImage).toBe('/blog/missing-og.png') |
| 72 | + expect(posts[0].ogImageWidth).toBeUndefined() |
| 73 | + expect(posts[0].ogImageHeight).toBeUndefined() |
| 74 | + }) |
| 75 | + |
| 76 | + it('still resolves a single post by slug', async () => { |
| 77 | + const registry = createContentRegistry({ contentDir, authorsDir }) |
| 78 | + |
| 79 | + const post = await registry.getPostBySlug('sharp-is-unavailable') |
| 80 | + |
| 81 | + expect(post?.title).toBe('Sharp Is Unavailable') |
| 82 | + }) |
| 83 | +}) |
0 commit comments