diff --git a/apps/ai-dial-admin/src/components/Common/StepperModalButtons/StepperModalButtons.tsx b/apps/ai-dial-admin/src/components/Common/StepperModalButtons/StepperModalButtons.tsx index e90f43c80..6ea81dd1b 100644 --- a/apps/ai-dial-admin/src/components/Common/StepperModalButtons/StepperModalButtons.tsx +++ b/apps/ai-dial-admin/src/components/Common/StepperModalButtons/StepperModalButtons.tsx @@ -4,6 +4,7 @@ import { FC } from 'react'; import { DialGhostButton, DialNeutralButton, DialPrimaryButton, Step, StepStatus } from '@epam/ai-dial-ui-kit'; import { IconArrowNarrowLeft, IconArrowNarrowRight } from '@tabler/icons-react'; +import classNames from 'classnames'; import { ButtonsI18nKey } from '@/src/constants/i18n'; import { BASE_BUTTON_ICON_PROPS } from '@/src/constants/main-layout'; @@ -31,7 +32,12 @@ const StepperModalButtons: FC = ({ steps, currentStep, onChangeStep, onFi }; return ( -
+
{currentStep?.id !== steps[0]?.id && ( = ({ testSuite, onChangeTestSuite, selectedAppType } }, [onChangeTestSuite, currentSuite]); const disableConfirm = useMemo(() => { - return !currentSuite.endpointRef?.method || !currentSuite.endpointRef?.relativeUrl; - }, [currentSuite.endpointRef?.method, currentSuite.endpointRef?.relativeUrl]); + return !currentSuite.endpointRef?.method || !currentSuite.endpointRef?.relativeUrlPattern; + }, [currentSuite.endpointRef?.method, currentSuite.endpointRef?.relativeUrlPattern]); return (testSuite?.endpointRef && !!Object.keys(testSuite?.endpointRef).length) || testSuite ? (
@@ -89,7 +89,7 @@ const MethodInfo: FC = ({ testSuite, onChangeTestSuite, selectedAppType } {testSuite?.endpointRef.method} )} - {testSuite?.endpointRef?.relativeUrl} + {testSuite?.endpointRef?.relativeUrlPattern}
diff --git a/apps/ai-dial-admin/src/components/TestSuites/Methods/MethodItem.tsx b/apps/ai-dial-admin/src/components/TestSuites/Methods/MethodItem.tsx index be684fb84..3f4d62065 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/Methods/MethodItem.tsx +++ b/apps/ai-dial-admin/src/components/TestSuites/Methods/MethodItem.tsx @@ -29,7 +29,7 @@ const MethodItem: FC = ({ index, item, isActive, onClick }) => { {item.method} - {item.relativeUrl} + {item.relativeUrlPattern}
); }; diff --git a/apps/ai-dial-admin/src/components/TestSuites/Methods/Methods.tsx b/apps/ai-dial-admin/src/components/TestSuites/Methods/Methods.tsx index dc8bbc14b..d96002b71 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/Methods/Methods.tsx +++ b/apps/ai-dial-admin/src/components/TestSuites/Methods/Methods.tsx @@ -48,7 +48,7 @@ const Methods: FC = ({ testSuite, selectedApplication, onChange, isCreate ...prev, endpointRef: { method: methods[index].method, - relativeUrl: methods[index].relativeUrl, + relativeUrlPattern: methods[index].relativeUrlPattern, }, })); } @@ -64,12 +64,14 @@ const Methods: FC = ({ testSuite, selectedApplication, onChange, isCreate const methods = generateMethodPathCombinations(data?.routes); setMethods(methods); const index = methods.findIndex( - (m) => m.method === testSuite.endpointRef?.method && m.relativeUrl === testSuite.endpointRef?.relativeUrl, + (m) => + m.method === testSuite.endpointRef?.method && + m.relativeUrlPattern === testSuite.endpointRef?.relativeUrlPattern, ); setActiveMethodIndex(index === -1 ? 0 : index); }); } - }, [fullApplication, selectedApplication, testSuite.endpointRef?.method, testSuite.endpointRef?.relativeUrl]); + }, [fullApplication, selectedApplication, testSuite.endpointRef?.method, testSuite.endpointRef?.relativeUrlPattern]); return (
@@ -86,7 +88,7 @@ const Methods: FC = ({ testSuite, selectedApplication, onChange, isCreate {!!methods.length && {t(TestSuitesI18nKey.Other)}} {methods.map((method, index) => ( ({ useI18n: () => (k: string) => k, })); vi.mock('@/src/components/TestSuites/utils/method', () => ({ - generateMethodPathCombinations: vi.fn(() => [ - { method: 'GET', relativeUrl: '/api' }, - { method: 'POST', relativeUrl: '/data' }, - ]), + generateMethodPathCombinations: (...args: any[]) => mockGenerateMethodPathCombinations(...args), })); vi.mock('@/src/app/[lang]/test-suites/actions', () => ({ - getDeployment: vi.fn(() => Promise.resolve({ deploymentId: 'd', $type: 't' })), + getDeployment: (...args: any[]) => mockGetDeployment(...args), })); vi.mock('@epam/ai-dial-ui-kit', () => ({ - DialCollapsibleSidebar: ({ children }: any) =>
{children}
, + DialCollapsibleSidebar: ({ children }: any) =>
{children}
, })); vi.mock('../MethodItem', () => ({ __esModule: true, - default: ({ item, index, onClick }: any) => ( - + default: ({ item, index, onClick, isActive }: any) => ( +
+ +
), })); vi.mock('../MethodInfo', () => ({ __esModule: true, - default: ({ testSuite }: any) =>
{JSON.stringify(testSuite?.endpointRef)}
, + default: ({ testSuite }: any) => ( +
+ {testSuite?.endpointRef?.method} +
+ ), })); describe('Methods component', () => { const onChange = vi.fn(); - const baseTestSuite: any = { endpointRef: {} }; - const selectedApplication: any = { deploymentId: 'd', $type: 't', routes: { r1: {} } }; + const mockDeployment = { + deploymentId: 'test-deployment', + $type: 'application', + routes: { + 'route-1': { path: '/api/users', methods: ['GET', 'POST'] }, + 'route-2': { path: '/api/data', methods: ['GET'] }, + }, + }; + + const mockMethods = [ + { method: 'GET', relativeUrlPattern: '/api/users' }, + { method: 'POST', relativeUrlPattern: '/api/users' }, + { method: 'GET', relativeUrlPattern: '/api/data' }, + ]; beforeEach(() => { onChange.mockClear(); + mockGetDeployment.mockClear(); + mockGenerateMethodPathCombinations.mockClear(); + mockGetDeployment.mockResolvedValue(mockDeployment); + mockGenerateMethodPathCombinations.mockReturnValue(mockMethods); }); - test('renders chat-completion item and generated methods', async () => { - render(); + test('renders chat-completion method as first item', async () => { + const testSuite: any = { endpointRef: {} }; + const selectedApplication: any = { deploymentId: 'test-deployment', $type: 'application' }; + + render(); await waitFor(() => { - expect(screen.getByTestId('method-0')).toBeInTheDocument(); - expect(screen.getByTestId('method-1')).toBeInTheDocument(); - expect(screen.getByTestId('method-2')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /POST.*\/api\/users/ })).toBeInTheDocument(); }); }); - test('clicking generated method calls onChange updater with correct endpointRef', async () => { - render(); + test('fetches deployment and generates methods on mount', async () => { + const testSuite: any = { endpointRef: {} }; + const selectedApplication: any = { deploymentId: 'test-deployment', $type: 'application' }; + + render(); await waitFor(() => { - expect(screen.getByTestId('method-1')).toBeInTheDocument(); + expect(mockGetDeployment).toHaveBeenCalledWith('test-deployment', 'application'); + expect(mockGenerateMethodPathCombinations).toHaveBeenCalledWith(mockDeployment.routes); }); + }); - const genMethodButton = screen.getByTestId('method-1'); - fireEvent.click(genMethodButton); + test('renders all generated methods after chat-completion', async () => { + const testSuite: any = { endpointRef: {} }; + const selectedApplication: any = { deploymentId: 'test-deployment', $type: 'application' }; + + render(); + + await waitFor(() => { + expect(screen.getByRole('button', { name: 'GET /api/users' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'POST /api/users' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'GET /api/data' })).toBeInTheDocument(); + }); + }); - expect(onChange).toHaveBeenCalledTimes(1); + test('sets active method based on existing endpointRef on mount', async () => { + const testSuite: any = { + endpointRef: { method: 'POST', relativeUrlPattern: '/api/users' }, + }; + const selectedApplication: any = { deploymentId: 'test-deployment', $type: 'application' }; - const updater = onChange.mock.calls[0][0]; - const newState = updater(baseTestSuite); + render(); - expect(newState.endpointRef).toEqual({ method: 'POST', relativeUrl: '/data' }); + await waitFor(() => { + expect(screen.getByRole('button', { name: 'POST /api/users' })).toBeInTheDocument(); + }); + + const activeButton = screen.getByRole('button', { name: 'POST /api/users' }); }); - test('clicking chat-completion item sets endpointRef to CHAT_COMPLETION_METHOD', async () => { - render(); + test('defaults to chat-completion when endpointRef does not match any method', async () => { + const testSuite: any = { + endpointRef: { method: 'DELETE', relativeUrlPattern: '/not-found' }, + }; + const selectedApplication: any = { deploymentId: 'test-deployment', $type: 'application' }; + + render(); await waitFor(() => { - expect(screen.getByTestId('method-0')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /POST.*\/api\/users/ })).toBeInTheDocument(); }); - const chatButton = screen.getByTestId('method-0'); - fireEvent.click(chatButton); + const chatButton = screen.getByRole('button', { name: /POST.*\/api\/users/ }); + }); - expect(onChange).toHaveBeenCalledTimes(1); + test('does not fetch deployment if already loaded', async () => { + const testSuite: any = { endpointRef: {} }; + const selectedApplication: any = { deploymentId: 'test-deployment', $type: 'application' }; + + const { rerender } = render( + , + ); + + await waitFor(() => { + expect(mockGetDeployment).toHaveBeenCalledTimes(1); + }); - const updater = onChange.mock.calls[0][0]; - const newState = updater(baseTestSuite); + rerender(); - expect(newState.endpointRef).toBeDefined(); + expect(mockGetDeployment).toHaveBeenCalledTimes(1); }); }); diff --git a/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/ImportFile.tsx b/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/ImportFile.tsx index 2f0bdb4c2..a78808659 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/ImportFile.tsx +++ b/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/ImportFile.tsx @@ -25,7 +25,7 @@ const ImportFileModal: FC = ({ selectedTestSuiteId, isModalOpen, onClose, const [isLoading, setIsLoading] = useState(false); const [selectedFile, setSelectedFile] = useState(null); - const [testCases, setTestCases] = useState(null); + const [testCases, setTestCases] = useState(null); const [columnDefs, setColumnDefs] = useState([]); const onChangeFile = (files: File[]) => { diff --git a/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/models.ts b/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/models.ts index 8579a636c..cfa7e161b 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/models.ts +++ b/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/models.ts @@ -1,10 +1,17 @@ export interface ImportPreview { + autoDetectedSchema: AutoDetectedSchema[]; detectedColumns: ColumnMapping[]; sampleRows: RowMapping[]; totalRows: number; warnings: ValidationWarning[]; } +export interface AutoDetectedSchema { + name: string; + type: string; + required: boolean; +} + export interface ColumnMapping { fieldName: string; headerName: string; @@ -15,8 +22,7 @@ export interface ColumnMapping { export interface RowMapping { enabled: boolean; valid: boolean; - facts: Record; - parameters: Record; + data: Record; validationWarnings: RowValidationWarning[]; testCaseName: string; } @@ -25,7 +31,7 @@ export interface RowValidationWarning { code: string; message: string; path: string; - source: string; + fieldName: string; } export interface ValidationWarning { diff --git a/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/utils.spec.ts b/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/utils.spec.ts index 65aa5eaca..3ffe6bf67 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/utils.spec.ts +++ b/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/utils.spec.ts @@ -12,6 +12,7 @@ describe('getGridDataFromImportPreview', () => { sampleRows: [], totalRows: 0, warnings: [], + autoDetectedSchema: [], }; const result = getGridDataFromImportPreview(importPreview); @@ -23,22 +24,21 @@ describe('getGridDataFromImportPreview', () => { test('should map sampleRows with facts and parameters to rowData correctly', () => { const importPreview: ImportPreview = { + autoDetectedSchema: [], detectedColumns: [{ fieldName: 'name', headerName: 'Name', inferredType: 'string', mappedTo: 'testCaseName' }], sampleRows: [ { enabled: true, valid: true, testCaseName: 'Test Case 1', - facts: { temperature: 0.7, model: 'gpt-4' }, - parameters: { maxTokens: 100, stream: true }, + data: { temperature: 0.7, model: 'gpt-4' }, validationWarnings: [], }, { enabled: false, valid: true, testCaseName: 'Test Case 2', - facts: { temperature: 0.5 }, - parameters: { maxTokens: 200 }, + data: { temperature: 0.5 }, validationWarnings: [], }, ], @@ -53,23 +53,18 @@ describe('getGridDataFromImportPreview', () => { enabled: true, valid: true, testCaseName: 'Test Case 1', - facts: { temperature: 0.7, model: 'gpt-4' }, - parameters: { maxTokens: 100, stream: true }, + data: { temperature: 0.7, model: 'gpt-4' }, validationWarnings: [], temperature: 0.7, model: 'gpt-4', - maxTokens: 100, - stream: true, }, { enabled: false, valid: true, testCaseName: 'Test Case 2', - facts: { temperature: 0.5 }, - parameters: { maxTokens: 200 }, + data: { temperature: 0.5 }, validationWarnings: [], temperature: 0.5, - maxTokens: 200, }, ]); }); @@ -80,6 +75,7 @@ describe('getGridDataFromImportPreview', () => { sampleRows: [], totalRows: 0, warnings: [], + autoDetectedSchema: [], }; const result = getGridDataFromImportPreview(importPreview); @@ -94,6 +90,7 @@ describe('getGridDataFromImportPreview', () => { sampleRows: [], totalRows: 0, warnings: [], + autoDetectedSchema: [], }; const result = getGridDataFromImportPreview(importPreview); @@ -104,14 +101,14 @@ describe('getGridDataFromImportPreview', () => { test('should handle rows with empty facts and parameters', () => { const importPreview: ImportPreview = { + autoDetectedSchema: [], detectedColumns: [{ fieldName: 'name', headerName: 'Name', inferredType: 'string', mappedTo: 'testCaseName' }], sampleRows: [ { enabled: true, valid: true, testCaseName: 'Test Case 1', - facts: {}, - parameters: {}, + data: {}, validationWarnings: [], }, ], @@ -126,8 +123,7 @@ describe('getGridDataFromImportPreview', () => { enabled: true, valid: true, testCaseName: 'Test Case 1', - facts: {}, - parameters: {}, + data: {}, validationWarnings: [], }, ]); @@ -135,14 +131,14 @@ describe('getGridDataFromImportPreview', () => { test('should correctly spread row properties when facts and parameters have same keys', () => { const importPreview: ImportPreview = { + autoDetectedSchema: [], detectedColumns: [], sampleRows: [ { enabled: true, valid: true, testCaseName: 'Test Case 1', - facts: { value: 100 }, - parameters: { value: 200 }, // Same key as facts + data: { value: 100 }, validationWarnings: [], }, ], @@ -152,7 +148,7 @@ describe('getGridDataFromImportPreview', () => { const result = getGridDataFromImportPreview(importPreview); - // parameters.value should override facts.value due to spread order - expect((result.rowData[0] as any).value).toBe(200); + // data.value should be correctly mapped + expect((result.rowData[0] as any).value).toBe(100); }); }); diff --git a/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/utils.ts b/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/utils.ts index b31f209e4..4e31fac0b 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/utils.ts +++ b/apps/ai-dial-admin/src/components/TestSuites/TestCases/Import/utils.ts @@ -11,5 +11,5 @@ export const getGridDataFromImportPreview = (importPreview: ImportPreview) => { })), ]; - return { colDefs, rowData: importPreview.sampleRows.map((row) => ({ ...row, ...row.facts, ...row.parameters })) }; + return { colDefs, rowData: importPreview.sampleRows.map((row) => ({ ...row, ...row.data })) }; }; diff --git a/apps/ai-dial-admin/src/components/TestSuites/TestCases/tests/TestCases.spec.tsx b/apps/ai-dial-admin/src/components/TestSuites/TestCases/tests/TestCases.spec.tsx index 3e069a6b3..b551c1962 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/TestCases/tests/TestCases.spec.tsx +++ b/apps/ai-dial-admin/src/components/TestSuites/TestCases/tests/TestCases.spec.tsx @@ -43,14 +43,14 @@ describe('TestCases', () => { const mockTestCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { temperature: 0.7, }, }, { - name: 'Test Case 2', - facts: { + testCaseName: 'Test Case 2', + data: { temperature: 0.5, }, }, @@ -145,12 +145,12 @@ describe('TestCases', () => { test('handles multiple test cases with different facts', async () => { const testCasesWithDifferentFacts: TestCase[] = [ { - name: 'Case 1', - facts: { temp: 0.5 }, + testCaseName: 'Case 1', + data: { temp: 0.5 }, }, { - name: 'Case 2', - facts: { model: 'gpt-4', tokens: 100 }, + testCaseName: 'Case 2', + data: { model: 'gpt-4', tokens: 100 }, }, ]; diff --git a/apps/ai-dial-admin/src/components/TestSuites/utils/columns.ts b/apps/ai-dial-admin/src/components/TestSuites/utils/columns.ts index 61576f835..2132f78c8 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/utils/columns.ts +++ b/apps/ai-dial-admin/src/components/TestSuites/utils/columns.ts @@ -1,11 +1,9 @@ import { TestCase } from '@/src/models/evaluation/test-suite'; import { TEST_CASES_COLUMN } from '@/src/constants/grid-columns/grid-columns'; -// TODO: add Parameters column after approve design for it - export const getTestCaseColumns = (testCases: TestCase[]) => { - const facts = testCases.reduce((acc: string[], testCase) => { - const testCaseFacts = Object.keys(testCase.facts || {}); + const data = testCases.reduce((acc: string[], testCase) => { + const testCaseFacts = Object.keys(testCase.data || {}); testCaseFacts.forEach((fact) => { if (!acc.includes(fact)) { acc.push(fact); @@ -16,7 +14,7 @@ export const getTestCaseColumns = (testCases: TestCase[]) => { return [ ...TEST_CASES_COLUMN, - ...facts.map((fact) => ({ + ...data.map((fact) => ({ field: fact, headerName: fact, })), diff --git a/apps/ai-dial-admin/src/components/TestSuites/utils/data.ts b/apps/ai-dial-admin/src/components/TestSuites/utils/data.ts index d454005a6..4e4728d23 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/utils/data.ts +++ b/apps/ai-dial-admin/src/components/TestSuites/utils/data.ts @@ -3,13 +3,10 @@ import { TestCase } from '@/src/models/evaluation/test-suite'; export const getTestCaseGridData = (testCases?: TestCase[] | null) => { return ( testCases?.reduce((acc: Record[], testCase: TestCase) => { - const factsData = Object.keys(testCase.facts || {}).reduce( - (factsAcc: Record, factKey: string) => { - factsAcc[factKey] = testCase.facts?.[factKey] as string; - return factsAcc; - }, - {}, - ); + const factsData = Object.keys(testCase.data || {}).reduce((factsAcc: Record, factKey: string) => { + factsAcc[factKey] = testCase.data?.[factKey] as string; + return factsAcc; + }, {}); acc.push({ ...testCase, diff --git a/apps/ai-dial-admin/src/components/TestSuites/utils/method.ts b/apps/ai-dial-admin/src/components/TestSuites/utils/method.ts index d48f6089b..aec34d038 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/utils/method.ts +++ b/apps/ai-dial-admin/src/components/TestSuites/utils/method.ts @@ -17,7 +17,7 @@ export const generateMethodPathCombinations = (input?: Record methods.forEach((method) => { result.push({ method, - relativeUrl: path, + relativeUrlPattern: path, }); }); }); diff --git a/apps/ai-dial-admin/src/components/TestSuites/utils/tests/columns.spec.ts b/apps/ai-dial-admin/src/components/TestSuites/utils/tests/columns.spec.ts index 013b79832..216e70975 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/utils/tests/columns.spec.ts +++ b/apps/ai-dial-admin/src/components/TestSuites/utils/tests/columns.spec.ts @@ -14,7 +14,7 @@ describe('getTestCaseColumns', () => { }); test('should return only base columns when first test case has no facts', () => { - const testCases: TestCase[] = [{ name: 'Test Case 1' }]; + const testCases: TestCase[] = [{ testCaseName: 'Test Case 1' }]; const result = getTestCaseColumns(testCases); @@ -25,8 +25,8 @@ describe('getTestCaseColumns', () => { test('should return only base columns when first test case has undefined facts', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: undefined, + testCaseName: 'Test Case 1', + data: undefined, }, ]; @@ -38,8 +38,8 @@ describe('getTestCaseColumns', () => { test('should return only base columns when first test case has empty facts object', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: {}, + testCaseName: 'Test Case 1', + data: {}, }, ]; @@ -52,8 +52,8 @@ describe('getTestCaseColumns', () => { test('should add columns for each fact in the first test case', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { temperature: 0.7, maxTokens: 100, }, @@ -72,8 +72,8 @@ describe('getTestCaseColumns', () => { test('should handle single fact', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { prompt: 'test prompt', }, }, @@ -88,8 +88,8 @@ describe('getTestCaseColumns', () => { test('should handle facts with various data types', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { stringFact: 'value', numberFact: 42, booleanFact: true, @@ -112,8 +112,8 @@ describe('getTestCaseColumns', () => { test('should handle facts with special characters in keys', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { 'fact-with-dash': 'value1', fact_with_underscore: 'value2', 'fact.with.dot': 'value3', @@ -132,8 +132,8 @@ describe('getTestCaseColumns', () => { test('should preserve the order of facts as they appear in the object', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { zFact: 'z', aFact: 'a', mFact: 'm', @@ -151,8 +151,8 @@ describe('getTestCaseColumns', () => { test('should correctly spread TEST_CASES_COLUMN at the beginning', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { customFact: 'value', }, }, diff --git a/apps/ai-dial-admin/src/components/TestSuites/utils/tests/data.spec.ts b/apps/ai-dial-admin/src/components/TestSuites/utils/tests/data.spec.ts index 528859045..7bdd99172 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/utils/tests/data.spec.ts +++ b/apps/ai-dial-admin/src/components/TestSuites/utils/tests/data.spec.ts @@ -20,17 +20,13 @@ describe('getTestCaseGridData', () => { }); test('should return test case without modification when it has no facts', () => { - const testCases: TestCase[] = [ - { - name: 'Test Case 1', - }, - ]; + const testCases: TestCase[] = [{ testCaseName: 'Test Case 1' }]; const result = getTestCaseGridData(testCases); expect(result).toEqual([ { - name: 'Test Case 1', + testCaseName: 'Test Case 1', }, ]); }); @@ -38,8 +34,8 @@ describe('getTestCaseGridData', () => { test('should return test case without modification when facts is undefined', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: undefined, + testCaseName: 'Test Case 1', + data: undefined, }, ]; @@ -47,8 +43,8 @@ describe('getTestCaseGridData', () => { expect(result).toEqual([ { - name: 'Test Case 1', - facts: undefined, + testCaseName: 'Test Case 1', + data: undefined, }, ]); }); @@ -56,8 +52,8 @@ describe('getTestCaseGridData', () => { test('should return test case without modification when facts is empty object', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: {}, + testCaseName: 'Test Case 1', + data: {}, }, ]; @@ -65,8 +61,8 @@ describe('getTestCaseGridData', () => { expect(result).toEqual([ { - name: 'Test Case 1', - facts: {}, + testCaseName: 'Test Case 1', + data: {}, }, ]); }); @@ -74,8 +70,8 @@ describe('getTestCaseGridData', () => { test('should spread facts into the result object', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { temperature: 0.7, maxTokens: 100, }, @@ -86,8 +82,8 @@ describe('getTestCaseGridData', () => { expect(result).toEqual([ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { temperature: 0.7, maxTokens: 100, }, @@ -100,8 +96,8 @@ describe('getTestCaseGridData', () => { test('should handle single fact', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { prompt: 'test prompt', }, }, @@ -111,8 +107,8 @@ describe('getTestCaseGridData', () => { expect(result).toEqual([ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { prompt: 'test prompt', }, prompt: 'test prompt', @@ -123,14 +119,14 @@ describe('getTestCaseGridData', () => { test('should process multiple test cases', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { temperature: 0.7, }, }, { - name: 'Test Case 2', - facts: { + testCaseName: 'Test Case 2', + data: { temperature: 0.5, model: 'gpt-4', }, @@ -141,15 +137,15 @@ describe('getTestCaseGridData', () => { expect(result.length).toBe(2); expect(result[0]).toEqual({ - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { temperature: 0.7, }, temperature: 0.7, }); expect(result[1]).toEqual({ - name: 'Test Case 2', - facts: { + testCaseName: 'Test Case 2', + data: { temperature: 0.5, model: 'gpt-4', }, @@ -161,20 +157,20 @@ describe('getTestCaseGridData', () => { test('should handle different fact values across test cases', () => { const testCases: TestCase[] = [ { - name: 'Case 1', - facts: { + testCaseName: 'Case 1', + data: { param1: 'value1', }, }, { - name: 'Case 2', - facts: { + testCaseName: 'Case 2', + data: { param2: 'value2', }, }, { - name: 'Case 3', - facts: {}, + testCaseName: 'Case 3', + data: {}, }, ]; @@ -192,8 +188,8 @@ describe('getTestCaseGridData', () => { test('should handle various data types in facts', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { stringFact: 'text', numberFact: 42, booleanFact: true, @@ -215,8 +211,8 @@ describe('getTestCaseGridData', () => { test('should preserve all test case properties', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { temperature: 0.7, }, }, @@ -224,16 +220,16 @@ describe('getTestCaseGridData', () => { const result = getTestCaseGridData(testCases); - expect(result[0].name).toBe('Test Case 1'); - expect(result[0].facts).toEqual({ temperature: 0.7 }); + expect(result[0].testCaseName).toBe('Test Case 1'); + expect(result[0].data).toEqual({ temperature: 0.7 }); expect(result[0].temperature).toBe(0.7); }); test('should handle facts with special characters in keys', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { 'fact-with-dash': 'value1', fact_with_underscore: 'value2', 'fact.with.dot': 'value3', @@ -251,8 +247,8 @@ describe('getTestCaseGridData', () => { test('should handle null and undefined values in facts', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { nullValue: null, undefinedValue: undefined, }, @@ -268,8 +264,8 @@ describe('getTestCaseGridData', () => { test('should handle facts overriding test case properties', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { name: 'Overridden Name', }, }, @@ -279,7 +275,7 @@ describe('getTestCaseGridData', () => { // Facts are spread after testCase, so they override expect(result[0].name).toBe('Overridden Name'); - expect(result[0].facts).toEqual({ name: 'Overridden Name' }); + expect(result[0].data).toEqual({ name: 'Overridden Name' }); }); test('should handle large number of facts', () => { @@ -290,8 +286,8 @@ describe('getTestCaseGridData', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts, + testCaseName: 'Test Case 1', + data: facts, }, ]; @@ -306,17 +302,17 @@ describe('getTestCaseGridData', () => { test('should handle mixed test cases with and without facts', () => { const testCases: TestCase[] = [ { - name: 'Case 1', - facts: { + testCaseName: 'Case 1', + data: { param: 'value', }, }, { - name: 'Case 2', + testCaseName: 'Case 2', }, { - name: 'Case 3', - facts: { + testCaseName: 'Case 3', + data: { another: 'test', }, }, @@ -333,8 +329,8 @@ describe('getTestCaseGridData', () => { test('should not mutate original test cases', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { temperature: 0.7, }, }, @@ -349,8 +345,8 @@ describe('getTestCaseGridData', () => { test('should handle zero values in facts', () => { const testCases: TestCase[] = [ { - name: 'Test Case 1', - facts: { + testCaseName: 'Test Case 1', + data: { zeroNumber: 0, emptyString: '', falseBool: false, diff --git a/apps/ai-dial-admin/src/components/TestSuites/utils/tests/method.spec.ts b/apps/ai-dial-admin/src/components/TestSuites/utils/tests/method.spec.ts index c6bae9fb7..c122ff05e 100644 --- a/apps/ai-dial-admin/src/components/TestSuites/utils/tests/method.spec.ts +++ b/apps/ai-dial-admin/src/components/TestSuites/utils/tests/method.spec.ts @@ -15,12 +15,12 @@ describe('generateMethodPathCombinations', () => { expect(generateMethodPathCombinations()).toEqual([]); expect(result).toEqual([ - { method: 'HEAD', relativeUrl: '/e' }, - { method: 'POST', relativeUrl: '/e' }, - { method: 'GET', relativeUrl: '/e' }, - { method: 'HEAD', relativeUrl: '/r' }, - { method: 'POST', relativeUrl: '/r' }, - { method: 'GET', relativeUrl: '/r' }, + { method: 'HEAD', relativeUrlPattern: '/e' }, + { method: 'POST', relativeUrlPattern: '/e' }, + { method: 'GET', relativeUrlPattern: '/e' }, + { method: 'HEAD', relativeUrlPattern: '/r' }, + { method: 'POST', relativeUrlPattern: '/r' }, + { method: 'GET', relativeUrlPattern: '/r' }, ]); }); @@ -39,8 +39,8 @@ describe('generateMethodPathCombinations', () => { const result = generateMethodPathCombinations(input); expect(result).toEqual([ - { method: 'GET', relativeUrl: '/api' }, - { method: 'POST', relativeUrl: '/data' }, + { method: 'GET', relativeUrlPattern: '/api' }, + { method: 'POST', relativeUrlPattern: '/data' }, ]); }); @@ -58,7 +58,7 @@ describe('generateMethodPathCombinations', () => { const result = generateMethodPathCombinations(input); - expect(result).toEqual([{ method: 'GET', relativeUrl: '/api' }]); + expect(result).toEqual([{ method: 'GET', relativeUrlPattern: '/api' }]); }); test('should skip routes with empty paths', () => { @@ -75,7 +75,7 @@ describe('generateMethodPathCombinations', () => { const result = generateMethodPathCombinations(input); - expect(result).toEqual([{ method: 'DELETE', relativeUrl: '/user' }]); + expect(result).toEqual([{ method: 'DELETE', relativeUrlPattern: '/user' }]); }); test('should return empty array when all routes have empty methods or paths', () => { @@ -118,10 +118,10 @@ describe('generateMethodPathCombinations', () => { const result = generateMethodPathCombinations(input); expect(result).toHaveLength(6); // (2 methods * 2 paths) + (2 methods * 1 path) - expect(result).toContainEqual({ method: 'GET', relativeUrl: '/users' }); - expect(result).toContainEqual({ method: 'POST', relativeUrl: '/profiles' }); - expect(result).toContainEqual({ method: 'PUT', relativeUrl: '/posts' }); - expect(result).toContainEqual({ method: 'DELETE', relativeUrl: '/posts' }); + expect(result).toContainEqual({ method: 'GET', relativeUrlPattern: '/users' }); + expect(result).toContainEqual({ method: 'POST', relativeUrlPattern: '/profiles' }); + expect(result).toContainEqual({ method: 'PUT', relativeUrlPattern: '/posts' }); + expect(result).toContainEqual({ method: 'DELETE', relativeUrlPattern: '/posts' }); }); test('should handle routes with missing methods property', () => { diff --git a/apps/ai-dial-admin/src/models/evaluation/test-suite.ts b/apps/ai-dial-admin/src/models/evaluation/test-suite.ts index c733f081b..f307b5350 100644 --- a/apps/ai-dial-admin/src/models/evaluation/test-suite.ts +++ b/apps/ai-dial-admin/src/models/evaluation/test-suite.ts @@ -21,13 +21,18 @@ export interface TestSuiteDeploymentRef { export interface TestSuiteEndpointRef { method?: string; - relativeUrl?: string; + relativeUrlPattern?: string; parameters?: Record[]; requestBodySchema?: DialScheme; responseBodySchema?: DialScheme; } export interface TestCase { - name?: string; - facts?: Record; + testCaseName?: string; + updatedAt?: number; + valid?: boolean; + id: string; + enabled: boolean; + createdAt: number; + data?: Record; }