obsidian-voice-note/tests/openai-classify.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

76 lines
2.7 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { classify } from '../src/core/openai';
const res = (status: number, text = '') => ({ status, text });
describe('classify: 2xx', () => {
it('returns the text as is', () => {
expect(classify(res(200, '{"text":"привет"}'))).toEqual({ ok: true, text: 'привет' });
});
it('returns whitespace-only text unchanged (queue decides "no speech")', () => {
expect(classify(res(200, '{"text":" "}'))).toEqual({ ok: true, text: ' ' });
});
it.each(['<html>oops</html>', '{"foo":1}', '{"text":5}', 'null', '[]', '"str"'])(
'200 with %s → permanent',
(body) => {
expect(classify(res(200, body))).toEqual({
ok: false,
kind: 'permanent',
message: `HTTP 200: ответ без поля text: ${body}`,
});
},
);
it('cuts the body to 200 chars', () => {
const body = `<html>${'x'.repeat(1000)}`;
expect(classify(res(200, body))).toEqual({
ok: false,
kind: 'permanent',
message: `HTTP 200: ответ без поля text: ${body.slice(0, 200)}`,
});
});
it('empty 2xx body → permanent without snippet', () => {
expect(classify(res(204, ''))).toEqual({ ok: false, kind: 'permanent', message: 'HTTP 204: ответ без поля text' });
});
});
describe('classify: errors', () => {
it.each([301, 302, 399, 400, 401, 403, 404, 413, 415, 422, 499])('%i → permanent', (status) => {
expect(classify(res(status, 'x'))).toEqual({ ok: false, kind: 'permanent', message: `HTTP ${status}: x` });
});
it.each([100, 101, 408, 429, 500, 502, 503, 599, 600])('%i → temporary', (status) => {
expect(classify(res(status, 'x'))).toEqual({ ok: false, kind: 'temporary', message: `HTTP ${status}: x` });
});
it('takes error.message from an OpenAI-style body', () => {
expect(classify(res(401, '{"error":{"message":"Invalid API key"}}'))).toEqual({
ok: false,
kind: 'permanent',
message: 'HTTP 401: Invalid API key',
});
});
it.each(['{"error":{"message":""}}', '{"error":"bad"}', '{"error":null}'])(
'falls back to the body when error.message is unusable: %s',
(body) => {
expect(classify(res(400, body))).toEqual({ ok: false, kind: 'permanent', message: `HTTP 400: ${body}` });
},
);
it('cuts a plain body to 200 chars after trim', () => {
expect(classify(res(500, ` ${'y'.repeat(1000)}`))).toEqual({
ok: false,
kind: 'temporary',
message: `HTTP 500: ${'y'.repeat(200)}`,
});
});
it.each(['', ' \n '])('empty body %j → "HTTP <code>"', (body) => {
expect(classify(res(503, body))).toEqual({ ok: false, kind: 'temporary', message: 'HTTP 503' });
});
});