UNPKG

assistant-cloud

Version:

Cloud integration for Assistant UI

565 lines (465 loc) 20.9 kB
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; import { AssistantCloudFiles } from "../AssistantCloudFiles"; import { AssistantCloudAPI } from "../AssistantCloudAPI"; // Mock the AssistantCloudAPI to avoid making real HTTP requests (except for integration tests) vi.mock("../AssistantCloudAPI"); describe("AssistantCloudFiles", () => { let cloudFiles: AssistantCloudFiles; let mockApi: AssistantCloudAPI; beforeEach(() => { // Create a mock API instance mockApi = { makeRequest: vi.fn(), makeRawRequest: vi.fn(), _auth: { getAuthHeaders: vi.fn() }, _baseUrl: "https://backend.assistant-api.com", } as unknown as AssistantCloudAPI; // Create the AssistantCloudFiles instance with the mock API cloudFiles = new AssistantCloudFiles(mockApi); }); afterEach(() => { vi.clearAllMocks(); }); describe("pdfToImages", () => { /** * Tests successful PDF to images conversion with file_url * This matches the curl request structure provided */ it("should successfully convert PDF to images using file_url", async () => { const mockResponse = { success: true, urls: [ "https://aui-pdf-processing.5c52327048f352f85fb041947c406ab4.r2.cloudflarestorage.com/images/8eb81c61-dc76-48fd-ab66-25cd84a28c97/page-1.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=8698a7e98d990c6edd11fee3a9d0f3f0%2F20250606%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20250606T035428Z&X-Amz-Expires=3600&X-Amz-Signature=ea26cdd5ff7dc85eba12970137606484b9a8ab2c520f31ddba9cbe5941b20793&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject", "https://aui-pdf-processing.5c52327048f352f85fb041947c406ab4.r2.cloudflarestorage.com/images/8eb81c61-dc76-48fd-ab66-25cd84a28c97/page-2.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=8698a7e98d990c6edd11fee3a9d0f3f0%2F20250606%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20250606T035428Z&X-Amz-Expires=3600&X-Amz-Signature=3499237770cc4402a60e6cc9b8ce8bd460faade9adf456d2773009f7d07af9cb&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject", "https://aui-pdf-processing.5c52327048f352f85fb041947c406ab4.r2.cloudflarestorage.com/images/8eb81c61-dc76-48fd-ab66-25cd84a28c97/page-3.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=8698a7e98d990c6edd11fee3a9d0f3f0%2F20250606%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20250606T035429Z&X-Amz-Expires=3600&X-Amz-Signature=663ee073972f1b7b82cede9039ed4bc0a6c08118533a9586e871807baf032bc2&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject", ], message: "PDF successfully converted to images", }; // Mock the API call to return our expected response vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = { file_url: "https://files.testfile.org/PDF/10MB-TESTFILE.ORG.pdf", }; const result = await cloudFiles.pdfToImages(requestBody); // Verify the API was called correctly expect(mockApi.makeRequest).toHaveBeenCalledWith("/files/pdf-to-images", { method: "POST", body: requestBody, }); // Verify the response structure expect(result).toEqual(mockResponse); expect(result.success).toBe(true); expect(result.urls).toHaveLength(3); expect(result.message).toBe("PDF successfully converted to images"); expect(result.urls[0]).toContain("page-1.png"); expect(result.urls[1]).toContain("page-2.png"); expect(result.urls[2]).toContain("page-3.png"); }); /** * Tests successful PDF to images conversion with file_blob */ it("should successfully convert PDF to images using file_blob", async () => { const mockResponse = { success: true, urls: [ "https://example.com/converted-image-1.png", "https://example.com/converted-image-2.png", ], message: "PDF successfully converted to images", }; vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = { file_blob: "data:application/pdf;base64,JVBERi0xLjQKJdPr6eEKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCjIgMCBvYmoKPDwKL1R5cGUgL1BhZ2VzCi9LaWRzIFszIDAgUl0KL0NvdW50IDEKL01lZGlhQm94IFswIDAgNTk1IDg0Ml0KPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCAyIDAgUgovTWVkaWFCb3ggWzAgMCA1OTUgODQyXQovQ29udGVudHMgNCAwIFIKPj4KZW5kb2JqCjQgMCBvYmoKPDwKL0xlbmd0aCA0NAo+PgpzdHJlYW0KQlQKL0YxIDEyIFRmCjEwMCA3MDAgVGQKKFRlc3QgUERGKSBUagoKRVQKZW5kc3RyZWFtCmVuZG9iago1IDAgb2JqCjw8Ci9UeXBlIC9Gb250Ci9CYXNlRm9udCAvSGVsdmV0aWNhCi9TdWJ0eXBlIC9UeXBlMQo+PgplbmRvYmoKNiAwIG9iago8PAovVHlwZSAvRm9udERlc2NyaXB0b3IKL0ZvbnROYW1lIC9IZWx2ZXRpY2EKPj4KZW5kb2J4cmVmCjAgNwowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMDkgMDAwMDAgbiAKMDAwMDAwMDA3NCAwMDAwMCBuIAowMDAwMDAwMTMzIDAwMDAwIG4gCjAwMDAwMDAyMDQgMDAwMDAgbiAKMDAwMDAwMDI5OSAwMDAwMCBuIAowMDAwMDAwMzc2IDAwMDAwIG4gCnRyYWlsZXIKPDwKL1NpemUgNwovUm9vdCAxIDAgUgo+PgpzdGFydHhyZWYKNDM0CiUlRU9GCg==", }; const result = await cloudFiles.pdfToImages(requestBody); expect(mockApi.makeRequest).toHaveBeenCalledWith("/files/pdf-to-images", { method: "POST", body: requestBody, }); expect(result).toEqual(mockResponse); expect(result.success).toBe(true); expect(result.urls).toHaveLength(2); }); /** * Tests API error handling */ it("should handle API errors gracefully", async () => { const errorMessage = "Invalid PDF file"; vi.mocked(mockApi.makeRequest).mockRejectedValue(new Error(errorMessage)); const requestBody = { file_url: "https://invalid-url.com/not-a-pdf.txt", }; await expect(cloudFiles.pdfToImages(requestBody)).rejects.toThrow( errorMessage, ); expect(mockApi.makeRequest).toHaveBeenCalledWith("/files/pdf-to-images", { method: "POST", body: requestBody, }); }); /** * Tests handling of failed conversion response */ it("should handle failed conversion response", async () => { const mockResponse = { success: false, urls: [], message: "Failed to convert PDF: File too large", }; vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = { file_url: "https://example.com/huge-file.pdf", }; const result = await cloudFiles.pdfToImages(requestBody); expect(result.success).toBe(false); expect(result.urls).toHaveLength(0); expect(result.message).toBe("Failed to convert PDF: File too large"); }); /** * Tests that the method works with both file_url and file_blob undefined (edge case) */ it("should handle empty request body", async () => { const mockResponse = { success: false, urls: [], message: "No file provided", }; vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = {}; const result = await cloudFiles.pdfToImages(requestBody); expect(mockApi.makeRequest).toHaveBeenCalledWith("/files/pdf-to-images", { method: "POST", body: requestBody, }); expect(result.success).toBe(false); expect(result.urls).toHaveLength(0); }); /** * Tests that the method sends the correct request headers and structure * This ensures compatibility with the API key authentication shown in the curl example */ it("should make request to correct endpoint with proper structure", async () => { const mockResponse = { success: true, urls: ["https://example.com/image.png"], message: "Success", }; vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = { file_url: "https://files.testfile.org/PDF/10MB-TESTFILE.ORG.pdf", }; await cloudFiles.pdfToImages(requestBody); // Verify the endpoint and method are correct expect(mockApi.makeRequest).toHaveBeenCalledWith("/files/pdf-to-images", { method: "POST", body: requestBody, }); // Verify it was called exactly once expect(mockApi.makeRequest).toHaveBeenCalledTimes(1); }); /** * Tests that both file_url and file_blob can be provided simultaneously */ it("should handle request with both file_url and file_blob", async () => { const mockResponse = { success: true, urls: ["https://example.com/image.png"], message: "Success", }; vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = { file_url: "https://example.com/file.pdf", file_blob: "base64data...", }; const result = await cloudFiles.pdfToImages(requestBody); expect(mockApi.makeRequest).toHaveBeenCalledWith("/files/pdf-to-images", { method: "POST", body: requestBody, }); expect(result).toEqual(mockResponse); }); }); describe("generatePresignedUploadUrl", () => { /** * Tests successful generation of presigned upload URL * This matches the curl request structure provided */ it("should successfully generate presigned upload URL", async () => { const mockResponse = { success: true, signedUrl: "https://aui-cloud-attachments.5c52327048f352f85fb041947c406ab4.r2.cloudflarestorage.com/attachments/0204c5a7-cd09-470c-9488-96cf0be9db92.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=8698a7e98d990c6edd11fee3a9d0f3f0%2F20250606%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20250606T051813Z&X-Amz-Expires=900&X-Amz-Signature=b0d27e7bee6200eb1964ad1d7f4a8ad76db15a3d99bd4fb47ff4b0b08fee5ca5&X-Amz-SignedHeaders=content-length%3Bhost&x-amz-checksum-crc32=AAAAAA%3D%3D&x-amz-meta-original-filename=hello.pdf&x-amz-meta-project-id=proj_09369w56dnge&x-amz-meta-user-id=676767&x-amz-sdk-checksum-algorithm=CRC32&x-id=PutObject", expiresAt: "2025-06-06T05:33:13.322Z", publicUrl: "https://storage.assistant-api.com/attachments/0204c5a7-cd09-470c-9488-96cf0be9db92.pdf", }; // Mock the API call to return our expected response vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = { filename: "hello.pdf", }; const result = await cloudFiles.generatePresignedUploadUrl(requestBody); // Verify the API was called correctly expect(mockApi.makeRequest).toHaveBeenCalledWith( "/files/attachments/generate-presigned-upload-url", { method: "POST", body: requestBody, }, ); // Verify the response structure expect(result).toEqual(mockResponse); expect(result.success).toBe(true); expect(result.signedUrl).toContain("aui-cloud-attachments"); expect(result.signedUrl).toContain("X-Amz-Algorithm=AWS4-HMAC-SHA256"); expect(result.expiresAt).toBe("2025-06-06T05:33:13.322Z"); expect(result.publicUrl).toBe( "https://storage.assistant-api.com/attachments/0204c5a7-cd09-470c-9488-96cf0be9db92.pdf", ); }); /** * Tests generation with different file types */ it("should successfully generate presigned upload URL for different file types", async () => { const mockResponse = { success: true, signedUrl: "https://aui-cloud-attachments.5c52327048f352f85fb041947c406ab4.r2.cloudflarestorage.com/attachments/test-image.png", expiresAt: "2025-06-06T05:33:13.322Z", publicUrl: "https://storage.assistant-api.com/attachments/test-image.png", }; vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = { filename: "test-image.png", }; const result = await cloudFiles.generatePresignedUploadUrl(requestBody); expect(mockApi.makeRequest).toHaveBeenCalledWith( "/files/attachments/generate-presigned-upload-url", { method: "POST", body: requestBody, }, ); expect(result).toEqual(mockResponse); expect(result.success).toBe(true); expect(result.publicUrl).toContain("test-image.png"); }); /** * Tests API error handling */ it("should handle API errors gracefully", async () => { const errorMessage = "Invalid filename"; vi.mocked(mockApi.makeRequest).mockRejectedValue(new Error(errorMessage)); const requestBody = { filename: "", }; await expect( cloudFiles.generatePresignedUploadUrl(requestBody), ).rejects.toThrow(errorMessage); expect(mockApi.makeRequest).toHaveBeenCalledWith( "/files/attachments/generate-presigned-upload-url", { method: "POST", body: requestBody, }, ); }); /** * Tests handling of failed response */ it("should handle failed upload URL generation response", async () => { const mockResponse = { success: false, signedUrl: "", expiresAt: "", publicUrl: "", }; vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = { filename: "invalid-file-type.exe", }; const result = await cloudFiles.generatePresignedUploadUrl(requestBody); expect(result.success).toBe(false); expect(result.signedUrl).toBe(""); expect(result.publicUrl).toBe(""); }); /** * Tests that the method sends the correct request headers and structure */ it("should make request to correct endpoint with proper structure", async () => { const mockResponse = { success: true, signedUrl: "https://example.com/signed-url", expiresAt: "2025-06-06T05:33:13.322Z", publicUrl: "https://example.com/public-url", }; vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); const requestBody = { filename: "document.pdf", }; await cloudFiles.generatePresignedUploadUrl(requestBody); // Verify the endpoint and method are correct expect(mockApi.makeRequest).toHaveBeenCalledWith( "/files/attachments/generate-presigned-upload-url", { method: "POST", body: requestBody, }, ); // Verify it was called exactly once expect(mockApi.makeRequest).toHaveBeenCalledTimes(1); }); /** * Tests filename validation scenarios */ it("should handle various filename formats", async () => { const mockResponse = { success: true, signedUrl: "https://example.com/signed-url", expiresAt: "2025-06-06T05:33:13.322Z", publicUrl: "https://example.com/public-url", }; vi.mocked(mockApi.makeRequest).mockResolvedValue(mockResponse); // Test with filename containing spaces and special characters const requestBody = { filename: "my document (final version).pdf", }; const result = await cloudFiles.generatePresignedUploadUrl(requestBody); expect(mockApi.makeRequest).toHaveBeenCalledWith( "/files/attachments/generate-presigned-upload-url", { method: "POST", body: requestBody, }, ); expect(result).toEqual(mockResponse); }); }); // Integration test that actually calls the real API describe("Integration Tests", () => { /** * Integration test that actually calls the real API endpoint * This test requires real API credentials to be set in environment variables: * - AUI_API_KEY: Your API key (e.g., sk_aui_proj_...) * - AUI_USER_ID: Your user ID * - AUI_WORKSPACE_ID: Your workspace ID */ it.skipIf( !process.env.AUI_API_KEY || !process.env.AUI_USER_ID || !process.env.AUI_WORKSPACE_ID, )( "should actually convert PDF to images using real API", async () => { // Unmock all modules for this test to use real implementations vi.doUnmock("../AssistantCloudAPI"); vi.doUnmock("../AssistantCloudFiles"); vi.doUnmock("../AssistantCloud"); // Clear all mocks and reload modules vi.resetModules(); // Import real modules const { AssistantCloud: RealAssistantCloud } = await import( "../AssistantCloud" ); const realCloud = new RealAssistantCloud({ apiKey: process.env.AUI_API_KEY!, userId: process.env.AUI_USER_ID!, workspaceId: process.env.AUI_WORKSPACE_ID!, }); const requestBody = { file_url: "https://files.testfile.org/PDF/10MB-TESTFILE.ORG.pdf", }; console.log("Making API call to convert PDF..."); const result = await realCloud.files.pdfToImages(requestBody); console.log("API call result:", result); // Verify the response structure expect(result).toHaveProperty("success"); expect(result).toHaveProperty("urls"); expect(result).toHaveProperty("message"); if (result.success) { expect(Array.isArray(result.urls)).toBe(true); expect(result.urls.length).toBeGreaterThan(0); expect(typeof result.message).toBe("string"); // Verify URLs are valid image URLs result.urls.forEach((url) => { expect(url).toMatch(/^https:\/\/.+\.(png|jpg|jpeg)(\?.*)?$/i); }); } else { // If it fails, at least verify the error message is a string expect(typeof result.message).toBe("string"); console.log("API call failed:", result.message); } // Restore mocks after the test vi.doMock("../AssistantCloudAPI"); }, 60000, // 60 second timeout for real API calls ); /** * Integration test for generatePresignedUploadUrl that actually calls the real API endpoint * This test requires real API credentials to be set in environment variables: * - AUI_API_KEY: Your API key (e.g., sk_aui_proj_...) * - AUI_USER_ID: Your user ID * - AUI_WORKSPACE_ID: Your workspace ID */ it.skipIf( !process.env.AUI_API_KEY || !process.env.AUI_USER_ID || !process.env.AUI_WORKSPACE_ID, )( "should actually generate presigned upload URL using real API", async () => { // Unmock all modules for this test to use real implementations vi.doUnmock("../AssistantCloudAPI"); vi.doUnmock("../AssistantCloudFiles"); vi.doUnmock("../AssistantCloud"); // Clear all mocks and reload modules vi.resetModules(); // Import real modules const { AssistantCloud: RealAssistantCloud } = await import( "../AssistantCloud" ); const realCloud = new RealAssistantCloud({ apiKey: process.env.AUI_API_KEY!, userId: process.env.AUI_USER_ID!, workspaceId: process.env.AUI_WORKSPACE_ID!, }); const requestBody = { filename: "test-upload.pdf", }; console.log("Making API call to generate presigned upload URL..."); const result = await realCloud.files.generatePresignedUploadUrl(requestBody); console.log("API call result:", result); // Verify the response structure expect(result).toHaveProperty("success"); expect(result).toHaveProperty("signedUrl"); expect(result).toHaveProperty("expiresAt"); expect(result).toHaveProperty("publicUrl"); if (result.success) { expect(typeof result.signedUrl).toBe("string"); expect(typeof result.expiresAt).toBe("string"); expect(typeof result.publicUrl).toBe("string"); expect(result.signedUrl.length).toBeGreaterThan(0); expect(result.publicUrl.length).toBeGreaterThan(0); // Verify URLs are valid HTTPS URLs expect(result.signedUrl).toMatch(/^https:\/\/.+/); expect(result.publicUrl).toMatch(/^https:\/\/.+/); // Verify the signed URL contains expected AWS signature parameters expect(result.signedUrl).toContain("X-Amz-Algorithm"); expect(result.signedUrl).toContain("X-Amz-Signature"); // Verify expiresAt is a valid ISO date string expect(() => new Date(result.expiresAt)).not.toThrow(); } else { console.log( "API call failed - this may be expected for some test cases", ); } // Restore mocks after the test vi.doMock("../AssistantCloudAPI"); }, 30000, // 30 second timeout for real API calls ); }); });