2026-06-02 00:43:05 +03:00
|
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
2026-04-30 13:07:37 +03:00
|
|
|
|
|
|
|
|
import { createNotificationTemplateRuntime } from './template-runtime.js';
|
|
|
|
|
|
2026-06-02 00:43:05 +03:00
|
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
|
|
2026-04-30 13:07:37 +03:00
|
|
|
const createRuntime = (settings = {}) => createNotificationTemplateRuntime({
|
|
|
|
|
readSettingsFromDisk: async () => settings,
|
|
|
|
|
persistSettings: vi.fn(async () => {}),
|
|
|
|
|
buildOpenCodeUrl: (path) => path,
|
|
|
|
|
getOpenCodeAuthHeaders: () => ({}),
|
|
|
|
|
resolveGitBinaryForSpawn: () => 'git',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('notification template runtime zen models', () => {
|
2026-06-02 00:43:05 +03:00
|
|
|
afterEach(() => {
|
|
|
|
|
globalThis.fetch = originalFetch;
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-19 02:06:52 +03:00
|
|
|
it('returns no selectable zen models after provider retirement', async () => {
|
2026-04-30 13:07:37 +03:00
|
|
|
const runtime = createRuntime();
|
|
|
|
|
const models = await runtime.fetchFreeZenModels();
|
|
|
|
|
|
2026-05-19 02:06:52 +03:00
|
|
|
expect(models).toEqual([]);
|
2026-04-30 13:07:37 +03:00
|
|
|
});
|
|
|
|
|
|
2026-05-19 02:06:52 +03:00
|
|
|
it('preserves stored zen model value for compatibility without validation', async () => {
|
2026-04-30 13:07:37 +03:00
|
|
|
const runtime = createRuntime({ zenModel: 'trinity-large-preview-free' });
|
|
|
|
|
|
2026-05-19 02:06:52 +03:00
|
|
|
await expect(runtime.resolveZenModel()).resolves.toBe('trinity-large-preview-free');
|
2026-04-30 13:07:37 +03:00
|
|
|
});
|
|
|
|
|
});
|
2026-06-16 15:21:27 +03:00
|
|
|
|
|
|
|
|
describe('notification template message extraction', () => {
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
globalThis.fetch = originalFetch;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('excludes reasoning parts from payload message text', () => {
|
|
|
|
|
const runtime = createRuntime();
|
|
|
|
|
|
|
|
|
|
expect(runtime.extractLastMessageText({
|
|
|
|
|
properties: {
|
|
|
|
|
info: {
|
|
|
|
|
parts: [
|
|
|
|
|
{ type: 'reasoning', text: 'private chain of thought' },
|
|
|
|
|
{ type: 'text', text: 'final answer' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})).toBe('final answer');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('ignores untyped parts even when they contain text', () => {
|
|
|
|
|
const runtime = createRuntime();
|
|
|
|
|
|
|
|
|
|
expect(runtime.extractLastMessageText({
|
|
|
|
|
properties: {
|
|
|
|
|
info: {
|
|
|
|
|
parts: [
|
|
|
|
|
{ text: 'untyped text' },
|
|
|
|
|
{ content: 'untyped content' },
|
|
|
|
|
{ type: 'text', text: 'typed final answer' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})).toBe('typed final answer');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('excludes reasoning parts when fetching assistant messages', async () => {
|
|
|
|
|
const runtime = createRuntime();
|
|
|
|
|
globalThis.fetch = vi.fn(async () => new Response(JSON.stringify([
|
|
|
|
|
{
|
|
|
|
|
info: { id: 'msg-1', role: 'assistant', finish: 'stop' },
|
|
|
|
|
parts: [
|
|
|
|
|
{ type: 'reasoning', text: 'private chain of thought' },
|
|
|
|
|
{ type: 'text', text: 'final answer' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
])));
|
|
|
|
|
|
|
|
|
|
await expect(runtime.fetchLastAssistantMessageText('session-1', 'msg-1')).resolves.toBe('final answer');
|
|
|
|
|
});
|
|
|
|
|
});
|