mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-21 03:56:23 +08:00
@@ -330,3 +330,45 @@ Generated by [AVA](https://avajs.dev).
|
||||
],
|
||||
{},
|
||||
]
|
||||
|
||||
## should handle generateSessionTitle correctly under various conditions
|
||||
|
||||
> should generate title when conditions are met
|
||||
|
||||
{
|
||||
chatWithPromptCalled: undefined,
|
||||
exists: true,
|
||||
title: 'What is Machine Learning?',
|
||||
}
|
||||
|
||||
> should not generate title when session already has title
|
||||
|
||||
{
|
||||
chatWithPromptCalled: false,
|
||||
exists: true,
|
||||
title: 'Existing Title',
|
||||
}
|
||||
|
||||
> should not generate title when no user messages exist
|
||||
|
||||
{
|
||||
chatWithPromptCalled: false,
|
||||
exists: true,
|
||||
title: null,
|
||||
}
|
||||
|
||||
> should not generate title when no assistant messages exist
|
||||
|
||||
{
|
||||
chatWithPromptCalled: false,
|
||||
exists: true,
|
||||
title: null,
|
||||
}
|
||||
|
||||
> should use correct prompt for title generation
|
||||
|
||||
{
|
||||
content: `[user]: Explain quantum computing briefly␊
|
||||
[assistant]: Quantum computing uses quantum mechanics principles.`,
|
||||
promptName: 'Summary as title',
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -11,7 +11,11 @@ import { EventBus, JobQueue } from '../base';
|
||||
import { ConfigModule } from '../base/config';
|
||||
import { AuthService } from '../core/auth';
|
||||
import { QuotaModule } from '../core/quota';
|
||||
import { ContextCategories, WorkspaceModel } from '../models';
|
||||
import {
|
||||
ContextCategories,
|
||||
CopilotSessionModel,
|
||||
WorkspaceModel,
|
||||
} from '../models';
|
||||
import { CopilotModule } from '../plugins/copilot';
|
||||
import { CopilotContextService } from '../plugins/copilot/context';
|
||||
import {
|
||||
@@ -57,12 +61,13 @@ import { MockCopilotProvider } from './mocks';
|
||||
import { createTestingModule, TestingModule } from './utils';
|
||||
import { WorkflowTestCases } from './utils/copilot';
|
||||
|
||||
const test = ava as TestFn<{
|
||||
type Context = {
|
||||
auth: AuthService;
|
||||
module: TestingModule;
|
||||
db: PrismaClient;
|
||||
event: EventBus;
|
||||
workspace: WorkspaceModel;
|
||||
copilotSession: CopilotSessionModel;
|
||||
context: CopilotContextService;
|
||||
prompt: PromptService;
|
||||
transcript: CopilotTranscriptionService;
|
||||
@@ -78,7 +83,8 @@ const test = ava as TestFn<{
|
||||
html: CopilotCheckHtmlExecutor;
|
||||
json: CopilotCheckJsonExecutor;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
const test = ava as TestFn<Context>;
|
||||
let userId: string;
|
||||
|
||||
test.before(async t => {
|
||||
@@ -119,6 +125,7 @@ test.before(async t => {
|
||||
const db = module.get(PrismaClient);
|
||||
const event = module.get(EventBus);
|
||||
const workspace = module.get(WorkspaceModel);
|
||||
const copilotSession = module.get(CopilotSessionModel);
|
||||
const prompt = module.get(PromptService);
|
||||
const factory = module.get(CopilotProviderFactory);
|
||||
|
||||
@@ -136,6 +143,7 @@ test.before(async t => {
|
||||
t.context.db = db;
|
||||
t.context.event = event;
|
||||
t.context.workspace = workspace;
|
||||
t.context.copilotSession = copilotSession;
|
||||
t.context.prompt = prompt;
|
||||
t.context.factory = factory;
|
||||
t.context.session = session;
|
||||
@@ -1752,3 +1760,168 @@ test('should be able to manage workspace embedding', async t => {
|
||||
t.is(ret2.length, 0, 'should not match workspace context');
|
||||
}
|
||||
});
|
||||
|
||||
test('should handle generateSessionTitle correctly under various conditions', async t => {
|
||||
const { prompt, session, workspace, copilotSession } = t.context;
|
||||
|
||||
await prompt.set('test', 'model', [{ role: 'user', content: '{{content}}' }]);
|
||||
const createSession = async (
|
||||
options: {
|
||||
userMessage?: string;
|
||||
assistantMessage?: string;
|
||||
existingTitle?: string;
|
||||
} = {}
|
||||
) => {
|
||||
const ws = await workspace.create(userId);
|
||||
const sessionId = await session.create({
|
||||
docId: 'test-doc',
|
||||
workspaceId: ws.id,
|
||||
userId,
|
||||
promptName: 'test',
|
||||
pinned: false,
|
||||
});
|
||||
|
||||
if (options.existingTitle) {
|
||||
await copilotSession.update({
|
||||
userId,
|
||||
sessionId,
|
||||
title: options.existingTitle,
|
||||
});
|
||||
}
|
||||
|
||||
const chatSession = await session.get(sessionId);
|
||||
if (chatSession) {
|
||||
if (options.userMessage) {
|
||||
chatSession.push({
|
||||
role: 'user',
|
||||
content: options.userMessage,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
}
|
||||
if (options.assistantMessage) {
|
||||
chatSession.push({
|
||||
role: 'assistant',
|
||||
content: options.assistantMessage,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
}
|
||||
await chatSession.save();
|
||||
}
|
||||
|
||||
return sessionId;
|
||||
};
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: 'should generate title when conditions are met',
|
||||
setup: () =>
|
||||
createSession({
|
||||
userMessage: 'What is machine learning?',
|
||||
assistantMessage:
|
||||
'Machine learning is a subset of artificial intelligence.',
|
||||
}),
|
||||
mockFn: () => 'What is Machine Learning?',
|
||||
expectSnapshot: true,
|
||||
},
|
||||
{
|
||||
name: 'should not generate title when session already has title',
|
||||
setup: () =>
|
||||
createSession({
|
||||
userMessage: 'Test message',
|
||||
assistantMessage: 'Test response',
|
||||
existingTitle: 'Existing Title',
|
||||
}),
|
||||
mockFn: () => 'New Title',
|
||||
expectSnapshot: true,
|
||||
expectNotCalled: true,
|
||||
},
|
||||
{
|
||||
name: 'should not generate title when no user messages exist',
|
||||
setup: () =>
|
||||
createSession({ assistantMessage: 'Hello! How can I help you?' }),
|
||||
mockFn: () => 'New Title',
|
||||
expectSnapshot: true,
|
||||
expectNotCalled: true,
|
||||
},
|
||||
{
|
||||
name: 'should not generate title when no assistant messages exist',
|
||||
setup: () => createSession({ userMessage: 'What is AI?' }),
|
||||
mockFn: () => 'New Title',
|
||||
expectSnapshot: true,
|
||||
expectNotCalled: true,
|
||||
},
|
||||
{
|
||||
name: 'should handle errors gracefully',
|
||||
setup: () =>
|
||||
createSession({
|
||||
userMessage: 'Test question',
|
||||
assistantMessage: 'Test answer',
|
||||
}),
|
||||
mockFn: () => {
|
||||
throw new Error('Mock error for testing');
|
||||
},
|
||||
expectError: 'Mock error for testing',
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const sessionId = await testCase.setup();
|
||||
let chatWithPromptCalled = false;
|
||||
|
||||
const mockStub = Sinon.stub(session, 'chatWithPrompt').callsFake(
|
||||
async () => {
|
||||
chatWithPromptCalled = true;
|
||||
return testCase.mockFn();
|
||||
}
|
||||
);
|
||||
|
||||
if (testCase.expectError) {
|
||||
await t.throwsAsync(
|
||||
() => session.generateSessionTitle({ sessionId }),
|
||||
{ message: testCase.expectError },
|
||||
testCase.name
|
||||
);
|
||||
} else {
|
||||
await session.generateSessionTitle({ sessionId });
|
||||
|
||||
if (testCase.expectSnapshot) {
|
||||
const sessionState = await session.getSession(sessionId);
|
||||
t.snapshot(
|
||||
{
|
||||
chatWithPromptCalled: testCase.expectNotCalled
|
||||
? chatWithPromptCalled
|
||||
: undefined,
|
||||
title: sessionState?.title,
|
||||
exists: !!sessionState,
|
||||
},
|
||||
testCase.name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mockStub.restore();
|
||||
}
|
||||
|
||||
{
|
||||
const sessionId = await createSession({
|
||||
userMessage: 'Explain quantum computing briefly',
|
||||
assistantMessage: 'Quantum computing uses quantum mechanics principles.',
|
||||
});
|
||||
|
||||
let capturedArgs: any[] = [];
|
||||
Sinon.stub(session, 'chatWithPrompt').callsFake(async (...args) => {
|
||||
capturedArgs = args;
|
||||
return 'Quantum Computing Explained';
|
||||
});
|
||||
|
||||
await session.generateSessionTitle({ sessionId });
|
||||
|
||||
t.snapshot(
|
||||
{
|
||||
promptName: capturedArgs[0],
|
||||
content: capturedArgs[1]?.content,
|
||||
},
|
||||
'should use correct prompt for title generation'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -58,6 +58,7 @@ test.beforeEach(async t => {
|
||||
workspaceId: workspace.id,
|
||||
docId,
|
||||
userId: user.id,
|
||||
title: null,
|
||||
promptName: 'prompt-name',
|
||||
promptAction: null,
|
||||
});
|
||||
|
||||
@@ -82,6 +82,7 @@ const createTestSession = async (
|
||||
workspaceId: workspace.id,
|
||||
docId: null,
|
||||
pinned: false,
|
||||
title: null,
|
||||
promptName: TEST_PROMPTS.NORMAL,
|
||||
promptAction: null,
|
||||
...overrides,
|
||||
@@ -297,6 +298,7 @@ test('should pin and unpin sessions', async t => {
|
||||
promptName: 'test-prompt',
|
||||
promptAction: null,
|
||||
pinned: true,
|
||||
title: null,
|
||||
});
|
||||
|
||||
const firstSession = await copilotSession.get(firstSessionId);
|
||||
@@ -312,6 +314,7 @@ test('should pin and unpin sessions', async t => {
|
||||
promptName: 'test-prompt',
|
||||
promptAction: null,
|
||||
pinned: true,
|
||||
title: null,
|
||||
});
|
||||
|
||||
const sessionStatesAfterSecondPin = await getSessionStates(db, [
|
||||
@@ -796,6 +799,7 @@ test('should handle fork and session attachment operations', async t => {
|
||||
workspaceId: workspace.id,
|
||||
docId: forkConfig.docId,
|
||||
pinned: forkConfig.pinned,
|
||||
title: null,
|
||||
parentSessionId,
|
||||
prompt: { name: TEST_PROMPTS.NORMAL, action: null, model: 'gpt-4.1' },
|
||||
messages: [
|
||||
|
||||
Reference in New Issue
Block a user