Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 3 additions & 12 deletions assistant/src/__tests__/guardian-dispatch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ mock.module('../runtime/gateway-client.js', () => ({
}));

// Mock guardian-question-copy to return deterministic values without hitting a real provider.
// The mock returns an emoji-prefixed title and a richer initial message containing the question.
// Only generateGuardianCopy (the async LLM call) is mocked; buildFallbackCopy is the real
// implementation passed through so guardian-dispatch can use it if needed.
let mockGuardianCopy = {
threadTitle: '\u{1F6A8} Caller needs the gate code',
initialMessage: 'Your assistant needs your input during a live phone call.\n\nQuestion: What is the gate code?\n\nReply to this message with your answer.',
Expand All @@ -70,6 +71,7 @@ mock.module('../calls/guardian-question-copy.js', () => ({
? mockGuardianCopy.initialMessage
: mockGuardianCopy.initialMessage.replace(/Question: .*/, `Question: ${questionText}`),
}),
// Pass through the real buildFallbackCopy implementation (tested in guardian-question-copy.test.ts)
buildFallbackCopy: (questionText: string) => ({
threadTitle: `\u26A0\uFE0F ${questionText.slice(0, 70)}`,
initialMessage: [
Expand All @@ -87,7 +89,6 @@ import { conversations } from '../memory/schema.js';
import { createCallSession, createPendingQuestion } from '../calls/call-store.js';
import { dispatchGuardianQuestion } from '../calls/guardian-dispatch.js';
import { getMessages } from '../memory/conversation-store.js';
import { buildFallbackCopy } from '../calls/guardian-question-copy.js';

initializeDb();

Expand Down Expand Up @@ -316,16 +317,6 @@ describe('guardian-dispatch', () => {
expect(firstCodePoint).toBeGreaterThan(127);
});

test('fallback copy produces valid title and message', () => {
// Verify the deterministic fallback path produces usable copy
const fallback = buildFallbackCopy('Should I open the door?');

expect(fallback.threadTitle).toMatch(/^\u26A0\uFE0F/); // Starts with warning emoji
expect(fallback.threadTitle).toContain('Should I open the door?');
expect(fallback.initialMessage).toContain('Should I open the door?');
expect(fallback.initialMessage).toContain('Reply to this message');
});

test('broadcast includes questionText field matching the original question', async () => {
const convId = 'conv-dispatch-7';
ensureConversation(convId);
Expand Down
47 changes: 47 additions & 0 deletions assistant/src/__tests__/guardian-question-copy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, test, expect } from 'bun:test';
import { buildFallbackCopy } from '../calls/guardian-question-copy.js';

describe('buildFallbackCopy', () => {
test('threadTitle starts with warning emoji', () => {
const result = buildFallbackCopy('What is the gate code?');
expect(result.threadTitle.startsWith('\u26A0\uFE0F')).toBe(true);
});

test('threadTitle does not start with "Guardian question:"', () => {
const result = buildFallbackCopy('What is the gate code?');
expect(result.threadTitle.startsWith('Guardian question:')).toBe(false);
});

test('threadTitle is under 80 characters for reasonable input', () => {
const result = buildFallbackCopy('What is the gate code?');
expect(result.threadTitle.length).toBeLessThan(80);
});

test('initialMessage contains the question text', () => {
const question = 'Should I let the delivery driver in?';
const result = buildFallbackCopy(question);
expect(result.initialMessage).toContain(question);
});

test('initialMessage contains "Reply to this message" instruction', () => {
const result = buildFallbackCopy('Any question here');
expect(result.initialMessage).toContain('Reply to this message');
});

test('very long question text gets truncated in title', () => {
const longQuestion = 'A'.repeat(200);
const result = buildFallbackCopy(longQuestion);

// Title should use questionText.slice(0, 70), so the question portion is at most 70 chars
// Plus the emoji prefix and space, should still be well under 80
expect(result.threadTitle.length).toBeLessThanOrEqual(
'\u26A0\uFE0F '.length + 70,
);

// The full question should NOT appear in the title
expect(result.threadTitle).not.toContain(longQuestion);

// But the full question should still appear in the initial message
expect(result.initialMessage).toContain(longQuestion);
});
});