Files
RedditBot/tests/conversation-outcome-recorder.test.ts
T

58 lines
2.2 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import type {
ConversationOutcomesRepository,
MessagesRepository,
SentMessageRecord,
SuccessfulMessagesRepository,
} from '../src/database/repositories.js';
import { recordConversationReply } from '../src/services/conversation-outcome-recorder.js';
import { createMockLogger } from './mockLogger.js';
function message(): SentMessageRecord {
return {
id: 1,
postId: 'post-1',
recipient: 'alice',
subject: 'alice',
message: 'Hello',
sentAt: 1,
gotResponse: false,
gotImage: false,
responseCheckedAt: null,
strategyId: 'direct',
chatRoomId: 'room-1',
subreddit: 'example',
};
}
function dependencies() {
return {
messages: { markGotResponse: vi.fn() } as unknown as MessagesRepository,
successful: { insert: vi.fn(), existsByMessage: vi.fn(() => false) } as unknown as SuccessfulMessagesRepository,
outcomes: { record: vi.fn(), suppressRecipient: vi.fn() } as unknown as ConversationOutcomesRepository,
logger: createMockLogger(),
};
}
describe('recordConversationReply', () => {
it('records a first reply but excludes it from learning until engagement is reciprocal', () => {
const deps = dependencies();
expect(recordConversationReply(message(), 'Thanks!', { messageCount: 2, reciprocal: false }, deps)).toBe('unknown');
expect(deps.messages.markGotResponse).toHaveBeenCalledWith(1);
expect(deps.successful.insert).not.toHaveBeenCalled();
});
it('stores only reciprocal engaged conversations as learning examples', () => {
const deps = dependencies();
expect(recordConversationReply(message(), 'Tell me more', { messageCount: 4, reciprocal: true }, deps)).toBe('engaged');
expect(deps.successful.insert).toHaveBeenCalledWith(expect.objectContaining({ sentMessageId: 1 }));
});
it('permanently suppresses a recipient after a boundary response', () => {
const deps = dependencies();
expect(recordConversationReply(message(), 'Stop messaging me', { messageCount: 2 }, deps)).toBe('boundary');
expect(deps.outcomes.suppressRecipient).toHaveBeenCalledWith('alice', 'boundary');
expect(deps.successful.insert).not.toHaveBeenCalled();
});
});