From adc0db31eaa95019059359fcba2f4c6421eb2b35 Mon Sep 17 00:00:00 2001 From: ProgrammerIn-wonderland <3838shah@gmail.com> Date: Mon, 11 May 2026 20:43:24 -0400 Subject: [PATCH] add tests --- .../ai-image/ImageGenerationDriver.test.ts | 204 ++++++++++++++++++ .../ai-video/VideoGenerationDriver.test.ts | 183 ++++++++++++++++ 2 files changed, 387 insertions(+) diff --git a/src/backend/drivers/ai-image/ImageGenerationDriver.test.ts b/src/backend/drivers/ai-image/ImageGenerationDriver.test.ts index 14a3a4971..1307488e4 100644 --- a/src/backend/drivers/ai-image/ImageGenerationDriver.test.ts +++ b/src/backend/drivers/ai-image/ImageGenerationDriver.test.ts @@ -410,6 +410,210 @@ describe('ImageGenerationDriver.generate audit log', () => { }); }); +// ── puter_output_path ───────────────────────────────────────────── + +describe('ImageGenerationDriver.generate puter_output_path', () => { + const TEST_ACTOR: import('../../core/actor.js').Actor = { + user: { uuid: 'test-user-uuid', id: 42, username: 'testuser' }, + }; + + const withTestUser = (fn: () => T | Promise): Promise => + Promise.resolve(runWithContext({ actor: TEST_ACTOR }, fn)); + + it('throws 400 when puter_output_path resolves to root', async () => { + await expect( + withTestUser(() => + driver.generate({ + model: 'dall-e-2', + prompt: 'hi', + puter_output_path: '/', + } as never), + ), + ).rejects.toMatchObject({ statusCode: 400 }); + + expect(openaiImagesGenerateMock).not.toHaveBeenCalled(); + }); + + it('throws 400 when puter_output_path parent is root (e.g. /image.png)', async () => { + await expect( + withTestUser(() => + driver.generate({ + model: 'dall-e-2', + prompt: 'hi', + puter_output_path: '/image.png', + } as never), + ), + ).rejects.toMatchObject({ statusCode: 400 }); + + expect(openaiImagesGenerateMock).not.toHaveBeenCalled(); + }); + + it('throws 403 when ACL denies write access to the destination', async () => { + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockResolvedValueOnce(false); + + await expect( + withTestUser(() => + driver.generate({ + model: 'dall-e-2', + prompt: 'hi', + puter_output_path: '/testuser/somedir/image.png', + } as never), + ), + ).rejects.toMatchObject({ statusCode: 403 }); + + expect(openaiImagesGenerateMock).not.toHaveBeenCalled(); + }); + + it('ACL check runs BEFORE provider.generate so credits are not wasted on a denied path', async () => { + const callOrder: string[] = []; + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockImplementation(async () => { + callOrder.push('acl'); + return false; + }); + openaiImagesGenerateMock.mockImplementation(async () => { + callOrder.push('provider'); + return { data: [{ url: 'https://oai/img.png' }] }; + }); + + await expect( + withTestUser(() => + driver.generate({ + model: 'dall-e-2', + prompt: 'hi', + puter_output_path: '/testuser/dir/img.png', + } as never), + ), + ).rejects.toMatchObject({ statusCode: 403 }); + + expect(callOrder).toEqual(['acl']); + }); + + it('resolves ~ in puter_output_path to //', async () => { + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockResolvedValueOnce(true); + + const fsWriteSpy = vi.spyOn(server.services.fs, 'write'); + fsWriteSpy.mockResolvedValueOnce(undefined as never); + + openaiImagesGenerateMock.mockResolvedValueOnce({ + data: [{ url: 'https://oai/img.png' }], + }); + fetchSpy.mockResolvedValueOnce( + new Response(Buffer.from('fake-png'), { + status: 200, + headers: { 'content-type': 'image/png' }, + }), + ); + + await withTestUser(() => + driver.generate({ + model: 'dall-e-2', + prompt: 'hi', + puter_output_path: '~/images/out.png', + } as never), + ); + + expect(fsWriteSpy).toHaveBeenCalledTimes(1); + const [, writeArg] = fsWriteSpy.mock.calls[0]!; + expect( + (writeArg as { fileMetadata: { path: string } }).fileMetadata.path, + ).toBe('/testuser/images/out.png'); + }); + + it('writes the generated image to FS and still returns the result URL', async () => { + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockResolvedValueOnce(true); + + const fsWriteSpy = vi.spyOn(server.services.fs, 'write'); + fsWriteSpy.mockResolvedValueOnce(undefined as never); + + openaiImagesGenerateMock.mockResolvedValueOnce({ + data: [{ url: 'https://oai/img.png' }], + }); + fetchSpy.mockResolvedValueOnce( + new Response(Buffer.from('fake-png'), { + status: 200, + headers: { 'content-type': 'image/png' }, + }), + ); + + const result = await withTestUser(() => + driver.generate({ + model: 'dall-e-2', + prompt: 'hi', + puter_output_path: '/testuser/photos/out.png', + } as never), + ); + + expect(result).toBe('https://oai/img.png'); + expect(fsWriteSpy).toHaveBeenCalledTimes(1); + const [userId, writeArg] = fsWriteSpy.mock.calls[0]!; + expect(userId).toBe(42); + const meta = ( + writeArg as { + fileMetadata: { + path: string; + contentType: string; + overwrite: boolean; + }; + } + ).fileMetadata; + expect(meta.path).toBe('/testuser/photos/out.png'); + expect(meta.contentType).toBe('image/png'); + expect(meta.overwrite).toBe(true); + }); + + it('does not forward puter_output_path to the upstream provider call', async () => { + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockResolvedValueOnce(true); + + const fsWriteSpy = vi.spyOn(server.services.fs, 'write'); + fsWriteSpy.mockResolvedValueOnce(undefined as never); + + openaiImagesGenerateMock.mockResolvedValueOnce({ + data: [{ url: 'https://oai/img.png' }], + }); + fetchSpy.mockResolvedValueOnce( + new Response(Buffer.from('fake-png'), { + status: 200, + headers: { 'content-type': 'image/png' }, + }), + ); + + await withTestUser(() => + driver.generate({ + model: 'dall-e-2', + prompt: 'hi', + puter_output_path: '/testuser/dir/img.png', + } as never), + ); + + const sent = openaiImagesGenerateMock.mock.calls[0]![0]; + expect(sent.puter_output_path).toBeUndefined(); + }); + + it('throws 400 when actor has no user ID but puter_output_path is set', async () => { + const noIdActor: import('../../core/actor.js').Actor = { + user: { uuid: 'no-id-uuid', username: 'noone' }, + }; + await expect( + Promise.resolve( + runWithContext({ actor: noIdActor }, () => + driver.generate({ + model: 'dall-e-2', + prompt: 'hi', + puter_output_path: '/noone/dir/img.png', + } as never), + ), + ), + ).rejects.toMatchObject({ statusCode: 400 }); + + expect(openaiImagesGenerateMock).not.toHaveBeenCalled(); + }); +}); + // Avoid coupling the 'unused' XAI export to lint. The catalog reference // is also used implicitly by the routing tests above. void XAI_IMAGE_GENERATION_MODELS; diff --git a/src/backend/drivers/ai-video/VideoGenerationDriver.test.ts b/src/backend/drivers/ai-video/VideoGenerationDriver.test.ts index d8903ff79..c0303f0ab 100644 --- a/src/backend/drivers/ai-video/VideoGenerationDriver.test.ts +++ b/src/backend/drivers/ai-video/VideoGenerationDriver.test.ts @@ -448,3 +448,186 @@ describe('VideoGenerationDriver metering propagation', () => { expect(usageType).toMatch(/^openai:sora-2:/); }); }); + +// ── puter_output_path ───────────────────────────────────────────── + +describe('VideoGenerationDriver.generate puter_output_path', () => { + const TEST_ACTOR: import('../../core/actor.js').Actor = { + user: { uuid: 'test-user-uuid', id: 42, username: 'testuser' }, + }; + + const withTestUser = (fn: () => T | Promise): Promise => + Promise.resolve(runWithContext({ actor: TEST_ACTOR }, fn)); + + it('throws 400 when puter_output_path is root', async () => { + await expect( + withTestUser(() => + driver.generate({ + prompt: 'hi', + model: 'sora-2', + puter_output_path: '/', + } as never), + ), + ).rejects.toMatchObject({ statusCode: 400 }); + + expect(openaiVideosCreateMock).not.toHaveBeenCalled(); + }); + + it('throws 400 when puter_output_path parent is root (e.g. /video.mp4)', async () => { + await expect( + withTestUser(() => + driver.generate({ + prompt: 'hi', + model: 'sora-2', + puter_output_path: '/video.mp4', + } as never), + ), + ).rejects.toMatchObject({ statusCode: 400 }); + + expect(openaiVideosCreateMock).not.toHaveBeenCalled(); + }); + + it('throws 403 when ACL denies write access', async () => { + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockResolvedValueOnce(false); + + await expect( + withTestUser(() => + driver.generate({ + prompt: 'hi', + model: 'sora-2', + puter_output_path: '/testuser/videos/clip.mp4', + } as never), + ), + ).rejects.toMatchObject({ statusCode: 403 }); + + expect(openaiVideosCreateMock).not.toHaveBeenCalled(); + }); + + it('ACL check runs BEFORE provider.generate so credits are not wasted', async () => { + const callOrder: string[] = []; + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockImplementation(async () => { + callOrder.push('acl'); + return false; + }); + openaiVideosCreateMock.mockImplementation(async () => { + callOrder.push('provider'); + return openaiCompletedJob(); + }); + + await expect( + withTestUser(() => + driver.generate({ + prompt: 'hi', + model: 'sora-2', + puter_output_path: '/testuser/dir/clip.mp4', + } as never), + ), + ).rejects.toMatchObject({ statusCode: 403 }); + + expect(callOrder).toEqual(['acl']); + }); + + it('resolves ~ in puter_output_path to //', async () => { + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockResolvedValueOnce(true); + + const fsWriteSpy = vi.spyOn(server.services.fs, 'write'); + fsWriteSpy.mockResolvedValueOnce(undefined as never); + + openaiVideosCreateMock.mockResolvedValueOnce(openaiCompletedJob()); + openaiVideosDownloadMock.mockResolvedValueOnce(openaiDownload()); + + await withTestUser(() => + driver.generate({ + prompt: 'hi', + model: 'sora-2', + puter_output_path: '~/videos/clip.mp4', + } as never), + ); + + expect(fsWriteSpy).toHaveBeenCalledTimes(1); + const [, writeArg] = fsWriteSpy.mock.calls[0]!; + expect( + (writeArg as { fileMetadata: { path: string } }).fileMetadata.path, + ).toBe('/testuser/videos/clip.mp4'); + }); + + it('writes stream result to FS and returns a new stream to caller', async () => { + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockResolvedValueOnce(true); + + const fsWriteSpy = vi.spyOn(server.services.fs, 'write'); + fsWriteSpy.mockResolvedValueOnce(undefined as never); + + openaiVideosCreateMock.mockResolvedValueOnce(openaiCompletedJob()); + openaiVideosDownloadMock.mockResolvedValueOnce(openaiDownload()); + + const result = await withTestUser(() => + driver.generate({ + prompt: 'hi', + model: 'sora-2', + puter_output_path: '/testuser/videos/clip.mp4', + } as never), + ); + + expect(fsWriteSpy).toHaveBeenCalledTimes(1); + const [userId, writeArg] = fsWriteSpy.mock.calls[0]!; + expect(userId).toBe(42); + const meta = ( + writeArg as { + fileMetadata: { + path: string; + contentType: string; + overwrite: boolean; + }; + } + ).fileMetadata; + expect(meta.path).toBe('/testuser/videos/clip.mp4'); + expect(meta.overwrite).toBe(true); + + expect(result).toBeDefined(); + }); + + it('does not forward puter_output_path to the upstream provider call', async () => { + const aclCheckSpy = vi.spyOn(server.services.acl, 'check'); + aclCheckSpy.mockResolvedValueOnce(true); + + const fsWriteSpy = vi.spyOn(server.services.fs, 'write'); + fsWriteSpy.mockResolvedValueOnce(undefined as never); + + openaiVideosCreateMock.mockResolvedValueOnce(openaiCompletedJob()); + openaiVideosDownloadMock.mockResolvedValueOnce(openaiDownload()); + + await withTestUser(() => + driver.generate({ + prompt: 'hi', + model: 'sora-2', + puter_output_path: '/testuser/dir/clip.mp4', + } as never), + ); + + const sent = openaiVideosCreateMock.mock.calls[0]![0]; + expect(sent.puter_output_path).toBeUndefined(); + }); + + it('throws 400 when actor has no user ID but puter_output_path is set', async () => { + const noIdActor: import('../../core/actor.js').Actor = { + user: { uuid: 'no-id-uuid', username: 'noone' }, + }; + await expect( + Promise.resolve( + runWithContext({ actor: noIdActor }, () => + driver.generate({ + prompt: 'hi', + model: 'sora-2', + puter_output_path: '/noone/dir/clip.mp4', + } as never), + ), + ), + ).rejects.toMatchObject({ statusCode: 400 }); + + expect(openaiVideosCreateMock).not.toHaveBeenCalled(); + }); +});