Files
promptfoo--promptfoo/test/redteam/strategies/otherEncodings.test.ts
T
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

254 lines
9.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
addOtherEncodings,
EncodingType,
toCamelCase,
toEmojiEncoding,
toMorseCode,
toPigLatin,
} from '../../../src/redteam/strategies/otherEncodings';
import type { TestCase } from '../../../src/types/index';
describe('other encodings strategy', () => {
const testCases: TestCase[] = [
{
vars: {
prompt: 'Hello World! 123',
expected: 'normal value',
},
assert: [
{
type: 'equals',
value: 'expected value',
metric: 'original-metric',
},
],
},
];
describe('Morse code', () => {
it('should convert text to Morse code', () => {
const result = addOtherEncodings(testCases, 'prompt', EncodingType.MORSE);
expect(result[0].vars!.prompt).toBe(
'.... . .-.. .-.. --- / .-- --- .-. .-.. -.. -.-.-- / .---- ..--- ...--',
);
expect(result[0].assert?.[0].metric).toBe('original-metric/Morse');
// Check that other vars are not affected
expect(result[0].vars!.expected).toBe('normal value');
// Check that metadata and assertion are updated correctly
expect(result[0].metadata?.strategyId).toBe('morse');
expect(result[0].metadata?.encodingType).toBe(EncodingType.MORSE);
expect(result[0].metadata?.originalText).toBe('Hello World! 123');
});
it('should handle empty string', () => {
const emptyCase: TestCase[] = [
{
vars: { prompt: '' },
assert: [{ type: 'equals', value: '', metric: 'test' }],
},
];
const result = addOtherEncodings(emptyCase, 'prompt', EncodingType.MORSE);
expect(result[0].vars!.prompt).toBe('');
});
it('should handle special characters not in morse map', () => {
const specialCase: TestCase[] = [
{
vars: { prompt: 'Hello % ^ #' },
assert: [{ type: 'equals', value: '', metric: 'test' }],
},
];
const result = addOtherEncodings(specialCase, 'prompt', EncodingType.MORSE);
expect(result[0].vars!.prompt).toBe('.... . .-.. .-.. --- / % / ^ / #');
});
});
describe('Pig Latin', () => {
it('should convert text to Pig Latin', () => {
const result = addOtherEncodings(testCases, 'prompt', EncodingType.PIG_LATIN);
expect(result[0].vars!.prompt).toBe('elloHay orldWay! 123');
expect(result[0].assert?.[0].metric).toBe('original-metric/PigLatin');
// Check that other vars are not affected
expect(result[0].vars!.expected).toBe('normal value');
// Check that metadata and assertion are updated correctly
expect(result[0].metadata?.strategyId).toBe('piglatin');
expect(result[0].metadata?.encodingType).toBe(EncodingType.PIG_LATIN);
expect(result[0].metadata?.originalText).toBe('Hello World! 123');
});
it('should handle words with no vowels', () => {
const noVowelCase: TestCase[] = [
{
vars: { prompt: 'cry shy' },
assert: [{ type: 'equals', value: '', metric: 'test' }],
},
];
const result = addOtherEncodings(noVowelCase, 'prompt', EncodingType.PIG_LATIN);
expect(result[0].vars!.prompt).toBe('cryay shyay');
});
it('should handle words starting with numbers', () => {
const numericCase: TestCase[] = [
{
vars: { prompt: '123 hello 456world' },
assert: [{ type: 'equals', value: '', metric: 'test' }],
},
];
const result = addOtherEncodings(numericCase, 'prompt', EncodingType.PIG_LATIN);
expect(result[0].vars!.prompt).toBe('123 ellohay 456world');
});
it('should handle words with multiple punctuation', () => {
const punctuationCase: TestCase[] = [
{
vars: { prompt: 'hello!? world...' },
assert: [{ type: 'equals', value: '', metric: 'test' }],
},
];
const result = addOtherEncodings(punctuationCase, 'prompt', EncodingType.PIG_LATIN);
expect(result[0].vars!.prompt).toBe('ellohay!? orldway...');
});
it('should handle empty string', () => {
const emptyCase: TestCase[] = [
{
vars: { prompt: '' },
assert: [{ type: 'equals', value: '', metric: 'test' }],
},
];
const result = addOtherEncodings(emptyCase, 'prompt', EncodingType.PIG_LATIN);
expect(result[0].vars!.prompt).toBe('');
});
it('should handle words starting with vowels', () => {
const vowelCase: TestCase[] = [
{
vars: { prompt: 'eat apple ice' },
assert: [{ type: 'equals', value: '', metric: 'test' }],
},
];
const result = addOtherEncodings(vowelCase, 'prompt', EncodingType.PIG_LATIN);
expect(result[0].vars!.prompt).toBe('eatway appleway iceway');
});
});
describe('camelCase', () => {
it('should convert text to camelCase', () => {
const result = addOtherEncodings(testCases, 'prompt', EncodingType.CAMEL_CASE);
expect(result[0].vars!.prompt).toBe('helloWorld!123');
expect(result[0].assert?.[0].metric).toBe('original-metric/CamelCase');
expect(result[0].metadata?.strategyId).toBe('camelcase');
expect(result[0].metadata?.encodingType).toBe(EncodingType.CAMEL_CASE);
expect(result[0].metadata?.originalText).toBe('Hello World! 123');
});
});
describe('emoji encoding', () => {
it('should encode text using variation selectors', () => {
const result = addOtherEncodings(testCases, 'prompt', EncodingType.EMOJI);
const encoded = result[0].vars!.prompt as string;
const chars = Array.from(encoded);
expect(chars[0]).toBe('😊');
for (const ch of chars.slice(1)) {
const code = ch.codePointAt(0)!;
const valid = (code >= 0xfe00 && code <= 0xfe0f) || (code >= 0xe0100 && code <= 0xe01ef);
expect(valid).toBe(true);
}
// decode and verify round trip
const decodedBytes: number[] = [];
for (const ch of chars.slice(1)) {
const code = ch.codePointAt(0)!;
if (code >= 0xfe00 && code <= 0xfe0f) {
decodedBytes.push(code - 0xfe00);
} else if (code >= 0xe0100 && code <= 0xe01ef) {
decodedBytes.push(code - 0xe0100 + 16);
}
}
const decoded = Buffer.from(decodedBytes).toString('utf8');
expect(decoded).toBe(testCases[0].vars!.prompt);
expect(result[0].metadata?.strategyId).toBe('emoji');
expect(result[0].metadata?.encodingType).toBe(EncodingType.EMOJI);
});
});
describe('encoding type handling', () => {
it('should use Morse code as default encoding', () => {
const result = addOtherEncodings(testCases, 'prompt');
expect(result[0].vars!.prompt).toBe(
'.... . .-.. .-.. --- / .-- --- .-. .-.. -.. -.-.-- / .---- ..--- ...--',
);
expect(result[0].metadata?.encodingType).toBe(EncodingType.MORSE);
expect(result[0].metadata?.originalText).toBe('Hello World! 123');
});
it('should preserve other test case properties', () => {
const result = addOtherEncodings(testCases, 'prompt', EncodingType.MORSE);
expect(result[0].vars!.expected).toBe('normal value');
expect(result[0].metadata?.strategyId).toBe('morse');
expect(result[0].metadata?.originalText).toBe('Hello World! 123');
});
it('should handle invalid encoding type by defaulting to Morse', () => {
const result = addOtherEncodings(testCases, 'prompt', 'invalid' as EncodingType);
expect(result[0].vars!.prompt).toBe(
'.... . .-.. .-.. --- / .-- --- .-. .-.. -.. -.-.-- / .---- ..--- ...--',
);
expect(result[0].metadata?.originalText).toBe('Hello World! 123');
});
});
describe('direct encoding functions', () => {
it('should convert to morse code directly', () => {
expect(toMorseCode('SOS')).toBe('... --- ...');
expect(toMorseCode('hello@world.com')).toBe(
'.... . .-.. .-.. --- .--.-. .-- --- .-. .-.. -.. .-.-.- -.-. --- --',
);
});
it('should convert to pig latin directly', () => {
expect(toPigLatin('eat')).toBe('eatway');
expect(toPigLatin('pig')).toBe('igpay');
expect(toPigLatin('latin')).toBe('atinlay');
expect(toPigLatin('')).toBe('');
expect(toPigLatin('123')).toBe('123');
});
it('should convert to camelCase directly', () => {
expect(toCamelCase('hello world')).toBe('helloWorld');
expect(toCamelCase('Hello-World!')).toBe('hello-World!');
});
it('should convert to emoji encoding directly', () => {
const encoded = toEmojiEncoding('abc');
const chars = Array.from(encoded);
expect(chars[0]).toBe('😊');
const bytes: number[] = [];
for (const ch of chars.slice(1)) {
const code = ch.codePointAt(0)!;
if (code >= 0xfe00 && code <= 0xfe0f) {
bytes.push(code - 0xfe00);
} else if (code >= 0xe0100 && code <= 0xe01ef) {
bytes.push(code - 0xe0100 + 16);
}
}
expect(Buffer.from(bytes).toString('utf8')).toBe('abc');
});
it('should handle leading, trailing, and multiple spaces in toCamelCase', () => {
expect(toCamelCase(' hello world')).toBe('helloWorld');
expect(toCamelCase('hello world ')).toBe('helloWorld');
expect(toCamelCase(' hello world ')).toBe('helloWorld');
expect(toCamelCase('hello world')).toBe('helloWorld');
expect(toCamelCase(' multiple spaces here ')).toBe('multipleSpacesHere');
});
});
});