From a65f3b8e6be6ea2d340006bee0b150e9af19c567 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 2 Feb 2026 17:26:57 -0800 Subject: [PATCH] fix tests --- apps/sim/blocks/blocks.test.ts | 12 ++- .../executor/handlers/api/api-handler.test.ts | 26 +++++- apps/sim/tools/utils.test.ts | 79 ++++++++++++------- 3 files changed, 84 insertions(+), 33 deletions(-) diff --git a/apps/sim/blocks/blocks.test.ts b/apps/sim/blocks/blocks.test.ts index eb59b46c2..395e1128a 100644 --- a/apps/sim/blocks/blocks.test.ts +++ b/apps/sim/blocks/blocks.test.ts @@ -442,7 +442,17 @@ describe('Blocks Module', () => { }) it('should have valid output types', () => { - const validPrimitiveTypes = ['string', 'number', 'boolean', 'json', 'array', 'files', 'any'] + const validPrimitiveTypes = [ + 'string', + 'number', + 'boolean', + 'json', + 'array', + 'files', + 'file', + 'file[]', + 'any', + ] const blocks = getAllBlocks() for (const block of blocks) { for (const [key, outputConfig] of Object.entries(block.outputs)) { diff --git a/apps/sim/executor/handlers/api/api-handler.test.ts b/apps/sim/executor/handlers/api/api-handler.test.ts index 3af7fac6f..0f7f0186a 100644 --- a/apps/sim/executor/handlers/api/api-handler.test.ts +++ b/apps/sim/executor/handlers/api/api-handler.test.ts @@ -1,6 +1,7 @@ import '@sim/testing/mocks/executor' import { beforeEach, describe, expect, it, type Mock, vi } from 'vitest' +import { validateUrlWithDNS } from '@/lib/core/security/input-validation.server' import { BlockType } from '@/executor/constants' import { ApiBlockHandler } from '@/executor/handlers/api/api-handler' import type { ExecutionContext } from '@/executor/types' @@ -9,8 +10,13 @@ import { executeTool } from '@/tools' import type { ToolConfig } from '@/tools/types' import { getTool } from '@/tools/utils' +vi.mock('@/lib/core/security/input-validation.server', () => ({ + validateUrlWithDNS: vi.fn(), +})) + const mockGetTool = vi.mocked(getTool) const mockExecuteTool = executeTool as Mock +const mockValidateUrlWithDNS = vi.mocked(validateUrlWithDNS) describe('ApiBlockHandler', () => { let handler: ApiBlockHandler @@ -63,6 +69,12 @@ describe('ApiBlockHandler', () => { // Reset mocks using vi vi.clearAllMocks() + mockValidateUrlWithDNS.mockResolvedValue({ + isValid: true, + resolvedIP: '93.184.216.34', + originalHostname: 'example.com', + }) + // Set up mockGetTool to return the mockApiTool mockGetTool.mockImplementation((toolId) => { if (toolId === 'http_request') { @@ -130,8 +142,13 @@ describe('ApiBlockHandler', () => { it('should throw error for invalid URL format (no protocol)', async () => { const inputs = { url: 'example.com/api' } + mockValidateUrlWithDNS.mockResolvedValueOnce({ + isValid: false, + error: 'url must be a valid URL', + }) + await expect(handler.execute(mockContext, mockBlock, inputs)).rejects.toThrow( - 'Invalid URL: "example.com/api" - URL must include protocol (try "https://example.com/api")' + 'url must be a valid URL' ) expect(mockExecuteTool).not.toHaveBeenCalled() }) @@ -139,8 +156,13 @@ describe('ApiBlockHandler', () => { it('should throw error for generally invalid URL format', async () => { const inputs = { url: 'htp:/invalid-url' } + mockValidateUrlWithDNS.mockResolvedValueOnce({ + isValid: false, + error: 'url must use https:// protocol', + }) + await expect(handler.execute(mockContext, mockBlock, inputs)).rejects.toThrow( - /^Invalid URL: "htp:\/invalid-url" - URL must include protocol/ + 'url must use https:// protocol' ) expect(mockExecuteTool).not.toHaveBeenCalled() }) diff --git a/apps/sim/tools/utils.test.ts b/apps/sim/tools/utils.test.ts index 0507eda1c..43a5531da 100644 --- a/apps/sim/tools/utils.test.ts +++ b/apps/sim/tools/utils.test.ts @@ -1,5 +1,9 @@ -import { createMockFetch, loggerMock } from '@sim/testing' +import { createMockResponse, loggerMock } from '@sim/testing' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + secureFetchWithPinnedIP, + validateUrlWithDNS, +} from '@/lib/core/security/input-validation.server' import { transformTable } from '@/tools/shared/table' import type { ToolConfig } from '@/tools/types' import { @@ -12,6 +16,10 @@ import { import { executeRequest } from '@/tools/utils.server' vi.mock('@sim/logger', () => loggerMock) +vi.mock('@/lib/core/security/input-validation.server', () => ({ + validateUrlWithDNS: vi.fn(), + secureFetchWithPinnedIP: vi.fn(), +})) vi.mock('@/stores/settings/environment', () => { const mockStore = { @@ -406,11 +414,18 @@ describe('validateRequiredParametersAfterMerge', () => { describe('executeRequest', () => { let mockTool: ToolConfig - let mockFetch: ReturnType + const mockValidateUrlWithDNS = vi.mocked(validateUrlWithDNS) + const mockSecureFetchWithPinnedIP = vi.mocked(secureFetchWithPinnedIP) beforeEach(() => { - mockFetch = createMockFetch({ json: { result: 'success' }, status: 200 }) - global.fetch = mockFetch + mockValidateUrlWithDNS.mockResolvedValue({ + isValid: true, + resolvedIP: '93.184.216.34', + originalHostname: 'api.example.com', + }) + mockSecureFetchWithPinnedIP.mockResolvedValue( + createMockResponse({ json: { result: 'success' }, status: 200 }) + ) mockTool = { id: 'test-tool', @@ -441,11 +456,15 @@ describe('executeRequest', () => { headers: {}, }) - expect(mockFetch).toHaveBeenCalledWith('https://api.example.com', { - method: 'GET', - headers: {}, - body: undefined, - }) + expect(mockSecureFetchWithPinnedIP).toHaveBeenCalledWith( + 'https://api.example.com', + '93.184.216.34', + { + method: 'GET', + headers: {}, + body: undefined, + } + ) expect(mockTool.transformResponse).toHaveBeenCalled() expect(result).toEqual({ success: true, @@ -455,8 +474,6 @@ describe('executeRequest', () => { it.concurrent('should use default transform response if not provided', async () => { mockTool.transformResponse = undefined - const localMockFetch = createMockFetch({ json: { result: 'success' }, status: 200 }) - global.fetch = localMockFetch const result = await executeRequest('test-tool', mockTool, { url: 'https://api.example.com', @@ -471,13 +488,14 @@ describe('executeRequest', () => { }) it('should handle error responses', async () => { - const errorFetch = createMockFetch({ - ok: false, - status: 400, - statusText: 'Bad Request', - json: { message: 'Invalid input' }, - }) - global.fetch = errorFetch + mockSecureFetchWithPinnedIP.mockResolvedValueOnce( + createMockResponse({ + ok: false, + status: 400, + statusText: 'Bad Request', + json: { message: 'Invalid input' }, + }) + ) const result = await executeRequest('test-tool', mockTool, { url: 'https://api.example.com', @@ -493,8 +511,7 @@ describe('executeRequest', () => { }) it.concurrent('should handle network errors', async () => { - const errorFetch = vi.fn().mockRejectedValueOnce(new Error('Network error')) - global.fetch = errorFetch + mockSecureFetchWithPinnedIP.mockRejectedValueOnce(new Error('Network error')) const result = await executeRequest('test-tool', mockTool, { url: 'https://api.example.com', @@ -510,15 +527,16 @@ describe('executeRequest', () => { }) it('should handle JSON parse errors in error response', async () => { - const errorFetch = vi.fn().mockResolvedValueOnce({ + const errorResponse = createMockResponse({ ok: false, status: 500, statusText: 'Server Error', - json: async () => { - throw new Error('Invalid JSON') - }, }) - global.fetch = errorFetch + errorResponse.json = vi.fn(async () => { + throw new Error('Invalid JSON') + }) + errorResponse.text = vi.fn(async () => '') + mockSecureFetchWithPinnedIP.mockResolvedValueOnce(errorResponse) const result = await executeRequest('test-tool', mockTool, { url: 'https://api.example.com', @@ -548,11 +566,12 @@ describe('executeRequest', () => { }, } - const xmlFetch = createMockFetch({ - status: 200, - text: 'Mock XML response', - }) - global.fetch = xmlFetch + mockSecureFetchWithPinnedIP.mockResolvedValueOnce( + createMockResponse({ + status: 200, + text: 'Mock XML response', + }) + ) const result = await executeRequest('test-tool', toolWithTransform, { url: 'https://api.example.com',