obsidian-voice-note/tests/openai-redact.test.ts
Andrey Epifancev bfe44fa648 feat(core): provider settings, response classification, secret redaction
Refs #5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QKHeGuAYh4W1jNdjMConZa
2026-09-28 00:18:33 +04:00

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-');
});
});