InspectorRAGet / src /__tests__ /validators.test.ts
kpfadnis's picture
refactor: type consolidation, chat component promotion, generation UX fixes, converter cleanup
753fcc5
raw
history blame
9.82 kB
/**
*
* Copyright 2023-present InspectorRAGet Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**/
import { describe, it, expect } from 'vitest';
import { validateInputData } from '@/src/validators';
// --- Fixtures ---
function validData() {
return {
models: [{ modelId: 'm1', name: 'Model 1' }],
metrics: [
{
name: 'accuracy',
author: 'algorithm',
type: 'numerical',
range: [0, 1],
},
],
tasks: [
{
taskId: 't1',
taskType: 'generation',
input: 'Hello world',
},
],
results: [
{
taskId: 't1',
modelId: 'm1',
output: [{ role: 'assistant', content: 'Hi' }],
scores: { accuracy: { system: { value: 0.9 } } },
},
],
};
}
function validQAData() {
return {
models: [{ modelId: 'm1', name: 'Model 1' }],
metrics: [
{
name: 'faithfulness',
author: 'algorithm',
type: 'numerical',
range: [0, 1],
},
],
documents: [{ documentId: 'd1', text: 'Some document text' }],
tasks: [
{
taskId: 't1',
taskType: 'qa',
input: 'What is X?',
contexts: [{ documentId: 'd1' }],
},
],
results: [
{
taskId: 't1',
modelId: 'm1',
output: [{ role: 'assistant', content: 'X is...' }],
scores: { faithfulness: { system: { value: 0.8 } } },
},
],
};
}
// --- Tests ---
describe('validateInputData', () => {
it('accepts a valid generation dataset', () => {
const result = validateInputData(validData());
expect(result.valid).toBe(true);
expect(result.reasons).toHaveLength(0);
});
it('accepts a valid qa dataset', () => {
const result = validateInputData(validQAData());
expect(result.valid).toBe(true);
expect(result.reasons).toHaveLength(0);
});
// --- Models validation ---
it('rejects data missing models', () => {
const data = validData();
delete (data as any).models;
const result = validateInputData(data);
expect(result.valid).toBe(false);
expect(result.reasons).toContain("Missing mandatory 'models' information.");
});
it('rejects a model missing modelId', () => {
const data = validData();
delete (data.models[0] as any).modelId;
const result = validateInputData(data);
expect(result.valid).toBe(false);
expect(
result.reasons.some((r) => r.includes('models are incorrectly')),
).toBe(true);
});
it('rejects a model missing name', () => {
const data = validData();
delete (data.models[0] as any).name;
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
// --- Metrics validation ---
it('rejects data missing metrics', () => {
const data = validData();
delete (data as any).metrics;
const result = validateInputData(data);
expect(result.valid).toBe(false);
expect(result.reasons).toContain(
"Missing mandatory 'metrics' information.",
);
});
it('rejects a metric missing name', () => {
const data = validData();
delete (data.metrics[0] as any).name;
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('rejects a metric with invalid author', () => {
const data = validData();
(data.metrics[0] as any).author = 'robot';
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('rejects a metric with invalid type', () => {
const data = validData();
(data.metrics[0] as any).type = 'boolean';
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('rejects a numerical metric with majority aggregator', () => {
const data = validData();
(data.metrics[0] as any).aggregator = 'majority';
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('rejects a categorical metric without values', () => {
const data = validData();
data.metrics[0] = {
name: 'quality',
author: 'human',
type: 'categorical',
} as any;
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('accepts a categorical metric with valid values', () => {
const data = validData();
data.metrics[0] = {
name: 'quality',
author: 'human',
type: 'categorical',
values: [
{ value: 'good', numericValue: 1 },
{ value: 'bad', numericValue: 0 },
],
} as any;
const result = validateInputData(data);
expect(result.valid).toBe(true);
});
it('rejects a metric value missing the value field', () => {
const data = validData();
data.metrics[0] = {
name: 'quality',
author: 'human',
type: 'categorical',
values: [{ numericValue: 1 }],
} as any;
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('rejects a categorical metric value missing numericValue', () => {
const data = validData();
data.metrics[0] = {
name: 'quality',
author: 'human',
type: 'categorical',
values: [
{ value: 'good', numericValue: 1 },
{ value: 'bad' }, // missing numericValue
],
} as any;
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('accepts a numerical metric value without numericValue', () => {
// numericValue is only required for categorical; numerical metrics use value directly
const data = validData();
data.metrics[0] = {
name: 'score',
author: 'algorithm',
type: 'numerical',
range: [0, 1],
} as any;
const result = validateInputData(data);
expect(result.valid).toBe(true);
});
// --- Tasks validation ---
it('rejects data missing tasks', () => {
const data = validData();
delete (data as any).tasks;
const result = validateInputData(data);
expect(result.valid).toBe(false);
expect(result.reasons).toContain("Missing mandatory 'tasks' information.");
});
it('rejects a task missing taskId', () => {
const data = validData();
delete (data.tasks[0] as any).taskId;
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('rejects a task with invalid taskType', () => {
const data = validData();
(data.tasks[0] as any).taskType = 'summarization';
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('rejects a task missing input', () => {
const data = validData();
delete (data.tasks[0] as any).input;
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('rejects a qa task missing contexts', () => {
const data = validQAData();
delete (data.tasks[0] as any).contexts;
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
it('accepts rag task type', () => {
const data = validData();
data.tasks[0] = {
taskId: 't1',
taskType: 'rag',
input: [{ role: 'user', content: 'Hello' }],
} as any;
const result = validateInputData(data);
expect(result.valid).toBe(true);
});
// --- Legacy task type names are still accepted (auto-migrated by processor) ---
it('accepts legacy chat task type', () => {
const data = validData();
data.tasks[0] = {
taskId: 't1',
taskType: 'chat',
input: [{ role: 'user', content: 'Hello' }],
} as any;
const result = validateInputData(data);
expect(result.valid).toBe(true);
});
it('accepts legacy text_generation task type', () => {
const data = validData();
data.tasks[0] = {
taskId: 't1',
taskType: 'text_generation',
input: 'Hello',
} as any;
const result = validateInputData(data);
expect(result.valid).toBe(true);
});
it('accepts legacy json_generation task type', () => {
const data = validData();
data.tasks[0].taskType = 'json_generation' as any;
const result = validateInputData(data);
expect(result.valid).toBe(true);
});
// --- Documents validation ---
it('rejects qa tasks without documents section', () => {
const data = validQAData();
delete (data as any).documents;
const result = validateInputData(data);
expect(result.valid).toBe(false);
expect(result.reasons.some((r) => r.includes("'documents'"))).toBe(true);
});
it('rejects a document missing documentId', () => {
const data = validQAData();
delete (data.documents[0] as any).documentId;
const result = validateInputData(data);
expect(result.valid).toBe(false);
});
// --- Results validation ---
it('rejects data missing results', () => {
const data = validData();
delete (data as any).results;
const result = validateInputData(data);
expect(result.valid).toBe(false);
expect(result.reasons).toContain(
"Missing mandatory 'results' information.",
);
});
// --- Multiple errors ---
it('collects multiple validation errors', () => {
const result = validateInputData({});
expect(result.valid).toBe(false);
// Should report missing models, metrics, tasks, and evaluations
expect(result.reasons.length).toBeGreaterThanOrEqual(3);
});
});