Files
RedditBot/tests/tone-profiles.test.ts
jjbruntonandClaude Opus 4.7 46a213e3e0 Add subreddit tone profiles for message style guidance
Introduces a tone-profile system that lets the processor adapt outgoing
message style per-subreddit (and per-flair) instead of using one tone
for everything. Profiles act as a "tone mic" layered after the base
prompt and strategy override, so they shape voice without redefining
the whole message.

- src/config/tone-profiles.ts defines the catalog (default, dominant,
  submissive, playful, direct, ...) and a resolveTone() helper that
  falls back to the default profile for unknown keys.
- config.yaml maps subreddits to tone keys; the schema in
  src/config/index.ts validates it.
- src/services/processor.ts threads the resolved tone into the prompt
  build, picking based on subreddit and optional link flair.
- src/reddit/api.ts adds link_flair_text to RedditPost so the
  processor can use it for tone selection.
- src/ai/openrouter.ts accepts and forwards the tone-shaped prompt.
- public/index.html surfaces the active tone in the dashboard.
- Tests cover the catalog, config validation, and processor integration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 22:29:01 +01:00

73 lines
2.1 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
TONE_KEYS,
TONE_PROFILES,
resolveTone,
type ToneKey,
} from '../src/config/tone-profiles.js';
describe('Tone profiles', () => {
describe('TONE_KEYS', () => {
it('includes default', () => {
expect(TONE_KEYS).toContain('default');
});
it('includes all expected profile keys', () => {
const expected: ToneKey[] = [
'default',
'dominant',
'submissive',
'playful',
'direct',
'gentle',
'kink-friendly',
'teasing',
];
expect(TONE_KEYS).toEqual(expect.arrayContaining(expected));
});
it('defines no duplicate keys', () => {
expect(new Set(TONE_KEYS).size).toBe(TONE_KEYS.length);
});
});
describe('TONE_PROFILES', () => {
for (const key of TONE_KEYS) {
it(`has a profile for ${key}`, () => {
expect(TONE_PROFILES[key]).toBeDefined();
expect(TONE_PROFILES[key].id).toBe(key);
expect(typeof TONE_PROFILES[key].label).toBe('string');
expect(typeof TONE_PROFILES[key].promptGuidance).toBe('string');
});
}
it('default profile has empty guidance', () => {
expect(TONE_PROFILES.default.promptGuidance).toBe('');
});
it('non-default profiles have non-empty guidance', () => {
for (const key of TONE_KEYS) {
if (key === 'default') continue;
expect(TONE_PROFILES[key].promptGuidance.length).toBeGreaterThan(0);
}
});
});
describe('resolveTone', () => {
it('returns default for unknown tone string', () => {
const result = resolveTone('nonexistent');
expect(result.id).toBe('default');
});
it('returns the matching profile for valid keys', () => {
expect(resolveTone('dominant').id).toBe('dominant');
expect(resolveTone('submissive').id).toBe('submissive');
expect(resolveTone('kink-friendly').id).toBe('kink-friendly');
expect(resolveTone('gentle').id).toBe('gentle');
expect(resolveTone('teasing').id).toBe('teasing');
expect(resolveTone('playful').id).toBe('playful');
expect(resolveTone('direct').id).toBe('direct');
});
});
});