Refs #5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QKHeGuAYh4W1jNdjMConZa
41 lines
1.5 KiB
TypeScript
41 lines
1.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { redact } from '../src/core/openai';
|
|
|
|
const KEY = 'sk-test-123';
|
|
|
|
describe('redact', () => {
|
|
it('replaces every occurrence of the key', () => {
|
|
expect(redact(`a ${KEY} b ${KEY}`, { apiKey: KEY })).toBe('a *** b ***');
|
|
});
|
|
|
|
it('masks any Bearer token', () => {
|
|
expect(redact('Authorization: Bearer abc.def-1', { apiKey: KEY })).toBe('Authorization: Bearer ***');
|
|
});
|
|
|
|
it('leaves the message alone for an empty key', () => {
|
|
expect(redact('HTTP 401: nope', { apiKey: '' })).toBe('HTTP 401: nope');
|
|
});
|
|
|
|
it('cuts at the multipart boundary and trims trailing ": "', () => {
|
|
const b = '----voice-note-0000';
|
|
expect(redact(`HTTP 400: echo --${b}\r\nContent-Disposition: form-data`, { apiKey: KEY, boundary: b })).toBe(
|
|
'HTTP 400: echo',
|
|
);
|
|
expect(redact(`HTTP 400: --${b}\r\n`, { apiKey: KEY, boundary: b })).toBe('HTTP 400');
|
|
});
|
|
|
|
it('cuts at Content-Disposition regardless of case', () => {
|
|
expect(redact('HTTP 400: got content-disposition: form-data; name="file"', { apiKey: KEY })).toBe('HTTP 400: got');
|
|
});
|
|
|
|
it('caps the result at 300 chars', () => {
|
|
expect(redact('x'.repeat(500), { apiKey: KEY })).toHaveLength(300);
|
|
});
|
|
|
|
it('replaces the key before capping, so no key fragment survives the cut', () => {
|
|
const out = redact(`${'y'.repeat(295)}${KEY}`, { apiKey: KEY });
|
|
expect(out).toBe(`${'y'.repeat(295)}***`);
|
|
expect(out).not.toContain('sk-');
|
|
});
|
|
});
|