Files
wehub-resource-sync 0d3cb498a3
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
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
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) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

353 lines
11 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { Severity } from '../../src/redteam/constants';
import { StrategyConfigSchema } from '../../src/redteam/types';
import { createMockProvider } from '../factories/provider';
import type {
BaseRedteamMetadata,
PluginActionParams,
RedteamCliGenerateOptions,
RedteamFileConfig,
RedteamGenerateOptions,
RedteamPlugin,
RedteamPluginObject,
RedteamRunOptions,
RedteamStrategyObject,
SavedRedteamConfig,
SynthesizeOptions,
} from '../../src/redteam/types';
describe('redteam types', () => {
it('should create valid RedteamPluginObject', () => {
const pluginObj: RedteamPluginObject = {
id: 'test-plugin',
config: {
examples: ['example1', 'example2'],
graderExamples: [
{
output: 'test output',
pass: true,
score: 1,
reason: 'test reason',
},
],
severity: Severity.Low,
},
severity: Severity.Medium,
numTests: 5,
};
expect(pluginObj.id).toBe('test-plugin');
expect(pluginObj.severity).toBe(Severity.Medium);
expect(pluginObj.numTests).toBe(5);
});
it('should create valid RedteamPlugin string or object', () => {
const pluginStr: RedteamPlugin = 'test-plugin';
const pluginObj: RedteamPlugin = {
id: 'test-plugin',
severity: Severity.Low,
};
expect(typeof pluginStr).toBe('string');
expect(typeof pluginObj).toBe('object');
});
it('should create valid RedteamStrategyObject', () => {
const strategyObj: RedteamStrategyObject = {
id: 'test-strategy',
config: {
enabled: true,
plugins: ['plugin1', 'plugin2'],
customField: 'value',
},
};
expect(strategyObj.id).toBe('test-strategy');
expect(strategyObj.config?.enabled).toBe(true);
expect(strategyObj.config?.plugins).toEqual(['plugin1', 'plugin2']);
});
it('should create valid PluginActionParams', () => {
const provider = createMockProvider({ response: { output: 'test' } });
const params: PluginActionParams = {
provider,
purpose: 'test purpose',
injectVar: 'test_var',
n: 5,
delayMs: 1000,
config: {
examples: ['example'],
},
};
expect(params.provider.id()).toBe('test-provider');
expect(params.purpose).toBe('test purpose');
expect(params.n).toBe(5);
});
it('should create valid RedteamCliGenerateOptions', () => {
const options: RedteamCliGenerateOptions = {
cache: true,
write: true,
defaultConfig: {},
plugins: [
{
id: 'test-plugin',
numTests: 5,
},
],
strategies: ['test-strategy'],
injectVar: 'test_var',
maxConcurrency: 5,
};
expect(options.cache).toBe(true);
expect(options.write).toBe(true);
expect(options.plugins?.length).toBe(1);
});
it('should create valid RedteamFileConfig', () => {
const pluginKey = 'agentic' as keyof NonNullable<RedteamFileConfig['severity']>;
const config: RedteamFileConfig = {
entities: ['entity1', 'entity2'],
severity: {
[pluginKey]: Severity.High,
} as Record<string, Severity>,
language: 'en',
numTests: 10,
};
expect(config.entities).toEqual(['entity1', 'entity2']);
expect(config.severity?.[pluginKey]).toBe(Severity.High);
});
it('should create valid SynthesizeOptions', () => {
const options: SynthesizeOptions = {
language: 'en',
numTests: 5,
plugins: [
{
id: 'test-plugin',
numTests: 3,
},
],
prompts: ['prompt1', 'prompt2'],
strategies: [
{
id: 'test-strategy',
},
],
targetIds: ['label1', 'label2'],
maxConcurrency: 3,
};
expect(options.language).toBe('en');
expect(options.numTests).toBe(5);
expect(options.plugins).toHaveLength(1);
});
it('should create valid RedteamRunOptions', () => {
const options: RedteamRunOptions = {
id: 'test-run',
target: 'test-target',
cache: true,
maxConcurrency: 5,
delay: 1000,
remote: false,
progressBar: true,
};
expect(options.id).toBe('test-run');
expect(options.target).toBe('test-target');
expect(options.maxConcurrency).toBe(5);
});
it('should create RedteamRunOptions with description for custom scan names', () => {
const options: RedteamRunOptions = {
id: 'test-run',
config: 'config-uuid',
target: 'target-uuid',
description: 'My Custom Scan Name',
maxConcurrency: 5,
};
expect(options.description).toBe('My Custom Scan Name');
expect(options.config).toBe('config-uuid');
expect(options.target).toBe('target-uuid');
});
it('should create valid SavedRedteamConfig', () => {
const config: SavedRedteamConfig = {
description: 'Test config',
prompts: ['prompt1', 'prompt2'],
target: {
id: 'test-target',
},
plugins: ['plugin1', { id: 'plugin2' }],
strategies: ['strategy1'],
entities: ['entity1'],
maxConcurrency: 5,
applicationDefinition: {
purpose: 'test purpose',
features: 'key app features',
hasAccessTo: 'allowed data and systems',
doesNotHaveAccessTo: 'restricted data',
userTypes: 'admin, regular users',
securityRequirements: 'security controls',
exampleIdentifiers: 'sample IDs',
industry: 'healthcare',
sensitiveDataTypes: 'PHI, PII',
criticalActions: 'data deletion',
forbiddenTopics: 'restricted topics',
competitors: 'competitor list',
systemPrompt: 'test prompt',
redteamUser: 'test user',
accessToData: 'accessible data',
forbiddenData: 'restricted data',
accessToActions: 'allowed actions',
forbiddenActions: 'restricted actions',
connectedSystems: 'integrated systems',
},
};
expect(config.description).toBe('Test config');
expect(config.prompts).toHaveLength(2);
expect(config.plugins).toHaveLength(2);
expect(config.maxConcurrency).toBe(5);
expect(config.applicationDefinition.features).toBe('key app features');
expect(config.applicationDefinition.hasAccessTo).toBe('allowed data and systems');
expect(config.applicationDefinition.doesNotHaveAccessTo).toBe('restricted data');
expect(config.applicationDefinition.userTypes).toBe('admin, regular users');
expect(config.applicationDefinition.securityRequirements).toBe('security controls');
expect(config.applicationDefinition.exampleIdentifiers).toBe('sample IDs');
expect(config.applicationDefinition.industry).toBe('healthcare');
expect(config.applicationDefinition.sensitiveDataTypes).toBe('PHI, PII');
expect(config.applicationDefinition.criticalActions).toBe('data deletion');
expect(config.applicationDefinition.forbiddenTopics).toBe('restricted topics');
expect(config.applicationDefinition.competitors).toBe('competitor list');
});
it('should create valid BaseRedteamMetadata', () => {
const metadata: BaseRedteamMetadata = {
redteamFinalPrompt: 'final prompt',
messages: [{ role: 'user', content: 'test' }],
stopReason: 'complete',
redteamHistory: [{ prompt: 'test prompt', output: 'test output' }],
};
expect(metadata.redteamFinalPrompt).toBe('final prompt');
expect(metadata.messages).toHaveLength(1);
expect(metadata.stopReason).toBe('complete');
});
it('should create valid RedteamGenerateOptions', () => {
const emptyOptions: RedteamGenerateOptions = {};
const partialOptions: RedteamGenerateOptions = {
cache: true,
plugins: [
{
id: 'test-plugin',
numTests: 5,
},
],
maxConcurrency: 3,
injectVar: 'test_var',
language: 'en',
purpose: 'test purpose',
remote: false,
sharing: true,
testGenerationInstructions: 'test instructions',
};
expect(emptyOptions).toBeDefined();
expect(partialOptions.cache).toBe(true);
expect(partialOptions.plugins?.length).toBe(1);
expect(partialOptions.maxConcurrency).toBe(3);
expect(partialOptions.language).toBe('en');
expect(partialOptions.remote).toBe(false);
expect(partialOptions.sharing).toBe(true);
expect(partialOptions.testGenerationInstructions).toBe('test instructions');
});
describe('StrategyConfigSchema numTests validation', () => {
it('should accept valid positive numTests', () => {
const result = StrategyConfigSchema.safeParse({ numTests: 5 });
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.numTests).toBe(5);
}
});
it('should accept numTests: 0', () => {
const result = StrategyConfigSchema.safeParse({ numTests: 0 });
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.numTests).toBe(0);
}
});
it('should accept missing numTests (undefined)', () => {
const result = StrategyConfigSchema.safeParse({});
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.numTests).toBeUndefined();
}
});
it('should reject negative numTests', () => {
const result = StrategyConfigSchema.safeParse({ numTests: -1 });
expect(result.success).toBe(false);
});
it('should reject non-integer numTests', () => {
const result = StrategyConfigSchema.safeParse({ numTests: 1.5 });
expect(result.success).toBe(false);
});
it('should reject string numTests', () => {
const result = StrategyConfigSchema.safeParse({ numTests: '5' });
expect(result.success).toBe(false);
});
it('should accept numTests with other strategy config fields', () => {
const result = StrategyConfigSchema.safeParse({
enabled: true,
plugins: ['plugin1', 'plugin2'],
numTests: 10,
customField: 'value',
});
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.numTests).toBe(10);
expect(result.data.enabled).toBe(true);
expect(result.data.plugins).toEqual(['plugin1', 'plugin2']);
}
});
it('should reject Infinity numTests', () => {
const result = StrategyConfigSchema.safeParse({ numTests: Infinity });
expect(result.success).toBe(false);
});
it('should reject -Infinity numTests', () => {
const result = StrategyConfigSchema.safeParse({ numTests: -Infinity });
expect(result.success).toBe(false);
});
it('should reject NaN numTests', () => {
const result = StrategyConfigSchema.safeParse({ numTests: NaN });
expect(result.success).toBe(false);
});
it('should accept large but finite numTests', () => {
const result = StrategyConfigSchema.safeParse({ numTests: 10000 });
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.numTests).toBe(10000);
}
});
});
});