commit for the purpose of working on another machine

This commit is contained in:
Will Freeman
2026-07-19 13:37:11 -06:00
parent 4f2676adc0
commit ad01b5a64d
29 changed files with 1905 additions and 10 deletions
+170
View File
@@ -0,0 +1,170 @@
import { describe, it, expect } from 'bun:test';
import { AiScreeningClient } from './AiScreeningClient';
function makeStubOpenAi(create: (...args: any[]) => Promise<any>) {
return { chat: { completions: { create } } } as any;
}
const validResult = {
ai_category: 'media_press',
media_tier: 'big_media',
confidence: 0.9,
kb_reference: 'none',
suggested_action: 'escalate_urgent',
suggested_reply: '',
internal_note: 'Reporter from a national outlet asking for comment, no stated deadline.',
risk_flags: [],
};
function completionWith(content: string) {
return { choices: [{ message: { content } }] };
}
describe('AiScreeningClient.screen', () => {
it('sends the system prompt, structured response_format, and a user message with only topic/subject/message/senderEmailDomain', async () => {
let capturedArgs: any;
let capturedOptions: any;
const create = async (args: any, options: any) => {
capturedArgs = args;
capturedOptions = options;
return completionWith(JSON.stringify(validResult));
};
const client = new AiScreeningClient(makeStubOpenAi(create));
await client.screen({
topic: 'media',
subject: 'Feature pitch',
message: 'We would like to interview your team.',
senderEmailDomain: 'nytimes.com',
});
expect(capturedArgs.messages[0].role).toBe('system');
expect(capturedArgs.messages[0].content.length).toBeGreaterThan(0);
expect(capturedArgs.response_format.type).toBe('json_schema');
expect(capturedArgs.response_format.json_schema.strict).toBe(true);
const userContent = JSON.parse(capturedArgs.messages[1].content);
expect(userContent).toEqual({
topic: 'media',
subject: 'Feature pitch',
message: 'We would like to interview your team.',
senderEmailDomain: 'nytimes.com',
});
expect(capturedOptions.timeout).toBe(20_000);
});
it('constrains kb_reference to "none" plus whatever .md filenames are actually loaded from kb/', async () => {
let capturedArgs: any;
const create = async (args: any) => {
capturedArgs = args;
return completionWith(JSON.stringify(validResult));
};
const client = new AiScreeningClient(makeStubOpenAi(create));
await client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' });
const enumValues: string[] = capturedArgs.response_format.json_schema.schema.properties.kb_reference.enum;
expect(enumValues).toContain('none');
// Every non-"none" value must correspond to a real, loaded .md file — never an invented name.
for (const value of enumValues) {
if (value !== 'none') expect(value.endsWith('.md')).toBe(true);
}
});
it('parses a valid structured response into a ScreeningResult', async () => {
const create = async () => completionWith(JSON.stringify(validResult));
const client = new AiScreeningClient(makeStubOpenAi(create));
const result = await client.screen({
topic: 'media',
subject: 'x',
message: 'y',
senderEmailDomain: 'nytimes.com',
});
expect(result).toEqual(validResult);
});
it('throws when the SDK call rejects', async () => {
const create = async () => { throw new Error('OpenAI request failed: 500 boom'); };
const client = new AiScreeningClient(makeStubOpenAi(create));
await expect(client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' }))
.rejects.toThrow('OpenAI request failed: 500 boom');
});
it('throws when the response has no content', async () => {
const create = async () => ({ choices: [{ message: {} }] });
const client = new AiScreeningClient(makeStubOpenAi(create));
await expect(client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' }))
.rejects.toThrow('OpenAI screening response missing content');
});
it('throws when the content is not valid JSON', async () => {
const create = async () => completionWith('not json');
const client = new AiScreeningClient(makeStubOpenAi(create));
await expect(client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' }))
.rejects.toThrow('OpenAI screening response was not valid JSON');
});
it('throws when the parsed JSON fails schema validation', async () => {
const { ai_category, ...incomplete } = validResult;
const create = async () => completionWith(JSON.stringify(incomplete));
const client = new AiScreeningClient(makeStubOpenAi(create));
await expect(client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' }))
.rejects.toThrow('OpenAI screening response failed schema validation');
});
it('throws when a value is outside the enum (e.g. an unrecognized ai_category)', async () => {
const create = async () => completionWith(JSON.stringify({ ...validResult, ai_category: 'made_up_category' }));
const client = new AiScreeningClient(makeStubOpenAi(create));
await expect(client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' }))
.rejects.toThrow('OpenAI screening response failed schema validation');
});
it('throws when internal_note is an empty string, rather than silently allowing a blank note', async () => {
const create = async () => completionWith(JSON.stringify({ ...validResult, internal_note: '' }));
const client = new AiScreeningClient(makeStubOpenAi(create));
await expect(client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' }))
.rejects.toThrow('OpenAI screening response failed schema validation');
});
it('throws when suggested_action is draft_response but suggested_reply is empty, rather than silently creating a blank shared draft', async () => {
const create = async () => completionWith(JSON.stringify({
...validResult,
ai_category: 'camera_report',
suggested_action: 'draft_response',
suggested_reply: '',
}));
const client = new AiScreeningClient(makeStubOpenAi(create));
await expect(client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' }))
.rejects.toThrow('OpenAI screening response has an empty suggested_reply for a draft_response category');
});
it('allows an empty suggested_reply for non-draft_response actions', async () => {
const create = async () => completionWith(JSON.stringify(validResult)); // escalate_urgent, empty reply
const client = new AiScreeningClient(makeStubOpenAi(create));
const result = await client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' });
expect(result.suggested_reply).toBe('');
});
it('passes when suggested_action is draft_response and suggested_reply is populated', async () => {
const create = async () => completionWith(JSON.stringify({
...validResult,
ai_category: 'camera_report',
suggested_action: 'draft_response',
suggested_reply: 'Here is how to report it yourself...',
}));
const client = new AiScreeningClient(makeStubOpenAi(create));
const result = await client.screen({ topic: 'media', subject: 'x', message: 'y', senderEmailDomain: '' });
expect(result.suggested_reply).toBe('Here is how to report it yourself...');
});
});