Files
promptfoo--promptfoo/test/util/exportToFile/convertTestResultsToTableRow.test.ts
wehub-resource-sync 0d3cb498a3
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Waiting to run
Test and Publish Multi-arch Docker Image / test (push) Waiting to run
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Blocked by required conditions
Validate Renovate Config / Validate Renovate Configuration (push) Waiting to run
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

420 lines
12 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { convertTestResultsToTableRow } from '../../../src/util/exportToFile/index';
import type EvalResult from '../../../src/models/evalResult';
/**
* Creates a minimal mock EvalResult for testing.
* We use a partial type and cast to avoid needing the full EvalResult class.
*/
function createMockEvalResult(overrides: Partial<EvalResult> = {}): EvalResult {
const defaultResult = {
id: 'test-id',
testIdx: 0,
promptIdx: 0,
description: 'Test description',
testCase: {
vars: {},
},
prompt: {
raw: 'test prompt',
},
response: {
output: 'test output',
},
provider: {
id: 'test-provider',
label: 'Test Provider',
},
success: true,
score: 1,
latencyMs: 100,
error: undefined,
metadata: undefined,
namedScores: {},
gradingResult: undefined,
cost: 0,
};
return {
...defaultResult,
...overrides,
testCase: {
...defaultResult.testCase,
...(overrides.testCase || {}),
},
prompt: {
...defaultResult.prompt,
...(overrides.prompt || {}),
},
response: {
...defaultResult.response,
...(overrides.response || {}),
},
provider: {
...defaultResult.provider,
...(overrides.provider || {}),
},
} as EvalResult;
}
describe('convertTestResultsToTableRow', () => {
describe('basic functionality', () => {
it('should convert results to table row format', () => {
const results = [
createMockEvalResult({
testCase: { vars: { question: 'What is AI?' } },
}),
];
const varsForHeader = ['question'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.description).toBe('Test description');
expect(row.vars).toEqual(['What is AI?']);
expect(row.testIdx).toBe(0);
expect(row.outputs).toHaveLength(1);
expect(row.outputs[0].text).toBe('test output');
});
it('should handle multiple variables', () => {
const results = [
createMockEvalResult({
testCase: {
vars: {
question: 'What is AI?',
context: 'Some context',
temperature: 0.5,
},
},
}),
];
const varsForHeader = ['context', 'question', 'temperature'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['Some context', 'What is AI?', '0.5']);
});
it('should handle missing variables', () => {
const results = [
createMockEvalResult({
testCase: { vars: { question: 'What is AI?' } },
}),
];
const varsForHeader = ['question', 'missingVar'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['What is AI?', '']);
});
it('should handle object variables as JSON', () => {
const results = [
createMockEvalResult({
testCase: {
vars: {
config: { nested: 'value', count: 42 },
},
},
}),
];
const varsForHeader = ['config'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars[0]).toBe('{"nested":"value","count":42}');
});
});
describe('sessionId handling', () => {
it('should use sessionId from testCase.vars when present', () => {
const results = [
createMockEvalResult({
testCase: { vars: { sessionId: 'user-session-123' } },
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['user-session-123']);
});
it('should use metadata.sessionId when vars.sessionId is not present', () => {
const results = [
createMockEvalResult({
testCase: { vars: {} },
metadata: { sessionId: 'metadata-session-456' },
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['metadata-session-456']);
});
it('should use metadata.sessionId when vars.sessionId is empty string', () => {
const results = [
createMockEvalResult({
testCase: { vars: { sessionId: '' } },
metadata: { sessionId: 'metadata-session-789' },
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['metadata-session-789']);
});
it('should not overwrite non-empty vars.sessionId with metadata.sessionId', () => {
const results = [
createMockEvalResult({
testCase: { vars: { sessionId: 'user-provided-session' } },
metadata: { sessionId: 'should-be-ignored' },
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['user-provided-session']);
});
it('should return empty string when no sessionId is available', () => {
const results = [
createMockEvalResult({
testCase: { vars: {} },
metadata: {},
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['']);
});
});
describe('sessionIds array handling (multi-turn strategies)', () => {
it('should join sessionIds array with newlines', () => {
const results = [
createMockEvalResult({
testCase: { vars: {} },
metadata: { sessionIds: ['session-1', 'session-2', 'session-3'] },
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['session-1\nsession-2\nsession-3']);
});
it('should handle single-element sessionIds array', () => {
const results = [
createMockEvalResult({
testCase: { vars: {} },
metadata: { sessionIds: ['only-session'] },
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['only-session']);
});
it('should prefer sessionIds array over sessionId when both exist', () => {
const results = [
createMockEvalResult({
testCase: { vars: {} },
metadata: {
sessionId: 'single-session',
sessionIds: ['multi-1', 'multi-2'],
},
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['multi-1\nmulti-2']);
});
it('should fall back to sessionId when sessionIds is empty array', () => {
const results = [
createMockEvalResult({
testCase: { vars: {} },
metadata: {
sessionId: 'fallback-session',
sessionIds: [],
},
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['fallback-session']);
});
it('should ignore non-array sessionIds and use sessionId instead', () => {
const results = [
createMockEvalResult({
testCase: { vars: {} },
metadata: {
sessionId: 'fallback-session',
sessionIds: 'not-an-array' as unknown as string[],
},
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['fallback-session']);
});
it('should not use sessionIds array if vars.sessionId is provided', () => {
const results = [
createMockEvalResult({
testCase: { vars: { sessionId: 'user-provided' } },
metadata: { sessionIds: ['should-be-ignored-1', 'should-be-ignored-2'] },
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['user-provided']);
});
});
describe('combined scenarios', () => {
it('should handle sessionId with other variables', () => {
const results = [
createMockEvalResult({
testCase: {
vars: {
question: 'What is AI?',
context: 'Some context',
},
},
metadata: { sessionIds: ['session-a', 'session-b'] },
}),
];
const varsForHeader = ['context', 'question', 'sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['Some context', 'What is AI?', 'session-a\nsession-b']);
});
it('should handle edge case with undefined metadata', () => {
const results = [
createMockEvalResult({
testCase: { vars: { question: 'What is AI?' } },
metadata: undefined,
}),
];
const varsForHeader = ['question', 'sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.vars).toEqual(['What is AI?', '']);
});
it('should handle non-string sessionId values in metadata', () => {
const results = [
createMockEvalResult({
testCase: { vars: {} },
metadata: { sessionId: { complex: 'object' } as unknown as string },
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
// Should be JSON stringified
expect(row.vars[0]).toBe('{"complex":"object"}');
});
it('should handle non-string sessionId values in vars', () => {
const results = [
createMockEvalResult({
testCase: { vars: { sessionId: { complex: 'object' } as unknown as string } },
}),
];
const varsForHeader = ['sessionId'];
const row = convertTestResultsToTableRow(results, varsForHeader);
// Should be JSON stringified
expect(row.vars[0]).toBe('{"complex":"object"}');
});
});
describe('output formatting', () => {
it('should format successful output correctly', () => {
const results = [
createMockEvalResult({
success: true,
response: { output: 'The answer is 42' },
}),
];
const varsForHeader: string[] = [];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.outputs[0].text).toBe('The answer is 42');
expect(row.outputs[0].pass).toBe(true);
});
it('should format error output correctly', () => {
const results = [
createMockEvalResult({
success: false,
error: 'Provider error occurred',
response: { output: '' },
}),
];
const varsForHeader: string[] = [];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.outputs[0].text).toBe('Provider error occurred');
expect(row.outputs[0].pass).toBe(false);
});
it('should handle null output', () => {
const results = [
createMockEvalResult({
response: { output: null },
error: 'Null response',
}),
];
const varsForHeader: string[] = [];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.outputs[0].text).toBe('Null response');
});
it('should handle object output', () => {
const results = [
createMockEvalResult({
response: { output: { key: 'value', nested: { data: 123 } } },
}),
];
const varsForHeader: string[] = [];
const row = convertTestResultsToTableRow(results, varsForHeader);
expect(row.outputs[0].text).toBe('{"key":"value","nested":{"data":123}}');
});
});
});