import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { NextRequest } from 'next/server'; const mocks = vi.hoisted(() => ({ generateImage: vi.fn(), managed: vi.fn((_section: string, _providerId: string) => false), resolveApiKey: vi.fn(), resolveBaseUrl: vi.fn(), recordUsage: vi.fn(), })); vi.mock('@/lib/media/image-providers', () => ({ IMAGE_PROVIDERS: { seedream: { requiresApiKey: true } }, aspectRatioToDimensions: () => ({ width: 1024, height: 576 }), generateImage: (...args: unknown[]) => mocks.generateImage(...args), })); vi.mock('@/lib/server/provider-config', () => ({ isServerConfiguredProvider: (section: string, providerId: string) => mocks.managed(section, providerId), resolveImageApiKey: (...args: unknown[]) => mocks.resolveApiKey(...args), resolveImageBaseUrl: (...args: unknown[]) => mocks.resolveBaseUrl(...args), })); vi.mock('@/lib/server/usage-storage', () => ({ recordGenerationUsage: (...args: unknown[]) => mocks.recordUsage(...args), })); vi.mock('@/lib/server/ssrf-guard', () => ({ validateUrlForSSRF: vi.fn(async () => null), })); vi.mock('@/lib/logger', () => ({ createLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), }), })); async function postImage(headers: Record = {}) { const { POST } = await import('@/app/api/generate/image/route'); const request = new Request('http://localhost/api/generate/image', { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers }, body: JSON.stringify({ prompt: 'A classroom diagram' }), }); return POST(request as unknown as NextRequest); } describe('POST /api/generate/image provider access response', () => { beforeEach(() => { vi.resetModules(); vi.unstubAllEnvs(); mocks.generateImage.mockReset(); mocks.managed.mockReset().mockReturnValue(false); mocks.resolveApiKey.mockReset(); mocks.resolveBaseUrl.mockReset(); mocks.recordUsage.mockReset(); }); it('returns 403/FORBIDDEN and never calls the provider for a central denial', async () => { const { ServerManagedProviderRequiredError } = await import('@/lib/server/provider-access-policy'); mocks.resolveApiKey.mockImplementation(() => { throw new ServerManagedProviderRequiredError(); }); const response = await postImage({ 'x-image-provider': 'seedream', 'x-api-key': 'caller-key', 'x-base-url': 'https://caller.invalid/v1', }); expect(response.status).toBe(403); await expect(response.json()).resolves.toMatchObject({ success: false, errorCode: 'FORBIDDEN', }); expect(mocks.generateImage).not.toHaveBeenCalled(); }); it('keeps the existing successful resolution path unchanged', async () => { mocks.resolveApiKey.mockReturnValue('client-key'); mocks.resolveBaseUrl.mockReturnValue('https://client.example/v1'); mocks.generateImage.mockResolvedValue({ url: 'https://cdn.example/image.png' }); const response = await postImage({ 'x-image-provider': 'seedream', 'x-api-key': 'client-key', 'x-base-url': 'https://client.example/v1', }); expect(response.status).toBe(200); expect(mocks.generateImage).toHaveBeenCalledWith( expect.objectContaining({ providerId: 'seedream', apiKey: 'client-key', baseUrl: 'https://client.example/v1', }), expect.objectContaining({ prompt: 'A classroom diagram' }), ); }); });