mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-02 02:19:43 +00:00
Add reversible review states, audit history, and super-admin controls for catalogue entities without changing current client visibility. Stage 30 high-risk production packs for archival review, cap generated content at non-explicit language, and preserve a checksummed pre-change production catalogue snapshot.
95 lines
3.9 KiB
TypeScript
95 lines
3.9 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const create = vi.fn();
|
|
|
|
vi.mock('@/hooks/useAiConfig', () => ({
|
|
getCachedAiConfig: () => null,
|
|
preloadAiConfig: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('./client', () => ({
|
|
getOpenAI: () => ({ chat: { completions: { create } } }),
|
|
getModel: () => 'test-model',
|
|
getTemperature: (_purpose?: string, fallback?: number) => fallback ?? 0,
|
|
}));
|
|
|
|
import { SYSTEM_MESSAGES, TONE_INSTRUCTIONS } from './config';
|
|
import { analyzeQuestionDeletions } from './analyzers/deletions';
|
|
import { describeImage } from './analyzers/media';
|
|
import { analyzeQuestionText } from './analyzers/text';
|
|
import { generateQuestions } from './generators/questions';
|
|
import { polishContent } from './tools/polish';
|
|
|
|
const prohibitedLegacyPhrases = [
|
|
'adult content is allowed',
|
|
'adult content is expected and acceptable',
|
|
'do not over-sanitize',
|
|
'keep explicit terms',
|
|
];
|
|
|
|
function serializedMessages(): string {
|
|
const request = create.mock.calls[create.mock.calls.length - 1]?.[0];
|
|
return JSON.stringify(request?.messages ?? []).toLowerCase();
|
|
}
|
|
|
|
describe('universal catalogue AI safety ceiling', () => {
|
|
beforeEach(() => {
|
|
create.mockReset();
|
|
create.mockResolvedValue({
|
|
choices: [{ message: { content: '{"polished":"safe","suggestions":[],"deletions":[]}' } }],
|
|
});
|
|
});
|
|
|
|
it('keeps every generation level non-explicit', () => {
|
|
for (const level of [1, 2, 3, 4, 5] as const) {
|
|
const instructions = `${TONE_INSTRUCTIONS[level]} ${SYSTEM_MESSAGES[level]}`.toLowerCase();
|
|
expect(instructions).toMatch(/no sexual|not sexual|never sexually explicit/);
|
|
}
|
|
});
|
|
|
|
it('does not let the legacy explicit flag weaken polish safety', async () => {
|
|
await polishContent('legacy explicit copy', 'question', true);
|
|
|
|
const messages = serializedMessages();
|
|
expect(messages).toContain('legacy explicit flag');
|
|
expect(messages).toContain('universal catalogue safety ceiling');
|
|
for (const phrase of prohibitedLegacyPhrases) expect(messages).not.toContain(phrase);
|
|
});
|
|
|
|
it('keeps daring generation and the legacy crude option below the same ceiling', async () => {
|
|
await generateQuestions('Daring connection', 2, 5, undefined, undefined, true);
|
|
|
|
const messages = serializedMessages();
|
|
expect(messages).toContain('critical universal safety ceiling');
|
|
expect(messages).toContain('legacy crude-language option');
|
|
expect(messages).toContain('never more explicit content');
|
|
});
|
|
|
|
it('does not let the legacy explicit flag weaken text repair safety', async () => {
|
|
await analyzeQuestionText([{ id: 'question-1', text: 'legacy explicit copy' }], true);
|
|
|
|
const messages = serializedMessages();
|
|
expect(messages).toContain('legacy explicit flag');
|
|
expect(messages).toContain('universal catalogue safety ceiling');
|
|
for (const phrase of prohibitedLegacyPhrases) expect(messages).not.toContain(phrase);
|
|
});
|
|
|
|
it('routes legacy explicit content toward deletion', async () => {
|
|
await analyzeQuestionDeletions([{ id: 'question-1', text: 'legacy explicit copy' }], true);
|
|
|
|
const messages = serializedMessages();
|
|
expect(messages).toContain('legacy explicit flag');
|
|
expect(messages).toContain('delete developer-authored sexual acts');
|
|
for (const phrase of prohibitedLegacyPhrases) expect(messages).not.toContain(phrase);
|
|
});
|
|
|
|
it('classifies adult imagery as ineligible rather than acceptable', async () => {
|
|
await describeImage('data:image/jpeg;base64,test');
|
|
|
|
const messages = serializedMessages();
|
|
expect(messages).toContain('adult_content');
|
|
expect(messages).toContain('not appropriate for publication');
|
|
for (const phrase of prohibitedLegacyPhrases) expect(messages).not.toContain(phrase);
|
|
});
|
|
});
|