81 lines
2.9 KiB
JavaScript
81 lines
2.9 KiB
JavaScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|||
|
|
|
||
|
|
const writeObjectiveMock = vi.fn(async () => undefined);
|
||
|
|
const generateSmallModelTextMock = vi.fn(async () => ({ text: '' }));
|
||
|
|
|
||
|
|
vi.mock('./objectives.js', () => ({
|
||
|
|
GOAL_OBJECTIVE_CHAR_LIMIT: 5_000,
|
||
|
|
writeObjective: writeObjectiveMock,
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock('../small-model/index.js', () => ({
|
||
|
|
generateSmallModelText: generateSmallModelTextMock,
|
||
|
|
}));
|
||
|
|
|
||
|
|
const { buildGoalIntroText, createSessionGoal } = await import('./create.js');
|
||
|
|
|
||
|
|
describe('session goal creation', () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
writeObjectiveMock.mockReset().mockResolvedValue(undefined);
|
||
|
|
generateSmallModelTextMock.mockReset().mockResolvedValue({ text: '' });
|
||
|
|
});
|
||
|
|
|
||
|
|
it('writes the objective before patching active goal metadata', async () => {
|
||
|
|
const fetchMock = vi.fn(async () => ({ ok: true }));
|
||
|
|
const originalFetch = globalThis.fetch;
|
||
|
|
globalThis.fetch = fetchMock;
|
||
|
|
try {
|
||
|
|
const goal = await createSessionGoal({
|
||
|
|
baseUrl: 'http://opencode.test',
|
||
|
|
authHeaders: { Authorization: 'Bearer test' },
|
||
|
|
sessionID: 'ses_123',
|
||
|
|
directory: '/repo/app',
|
||
|
|
objective: 'Finish and verify the migration',
|
||
|
|
tokenBudget: 200_000,
|
||
|
|
providerID: 'openai',
|
||
|
|
modelID: 'gpt-5.5',
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(writeObjectiveMock).toHaveBeenCalledWith('ses_123', 'Finish and verify the migration');
|
||
|
|
expect(writeObjectiveMock.mock.invocationCallOrder[0]).toBeLessThan(fetchMock.mock.invocationCallOrder[0]);
|
||
|
|
expect(goal).toMatchObject({ objective: '', objectiveFile: true, status: 'active', tokenBudget: 200_000 });
|
||
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
||
|
|
'http://opencode.test/session/ses_123?directory=%2Frepo%2Fapp',
|
||
|
|
expect.objectContaining({ method: 'PATCH' }),
|
||
|
|
);
|
||
|
|
} finally {
|
||
|
|
globalThis.fetch = originalFetch;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it('falls back to inline metadata when objective storage fails', async () => {
|
||
|
|
writeObjectiveMock.mockRejectedValueOnce(new Error('disk unavailable'));
|
||
|
|
const fetchMock = vi.fn(async () => ({ ok: true }));
|
||
|
|
const originalFetch = globalThis.fetch;
|
||
|
|
globalThis.fetch = fetchMock;
|
||
|
|
try {
|
||
|
|
await createSessionGoal({
|
||
|
|
baseUrl: 'http://opencode.test',
|
||
|
|
authHeaders: {},
|
||
|
|
sessionID: 'ses_123',
|
||
|
|
directory: '/repo/app',
|
||
|
|
objective: 'Finish the migration',
|
||
|
|
onWarning: vi.fn(),
|
||
|
|
});
|
||
|
|
|
||
|
|
const payload = JSON.parse(fetchMock.mock.calls[0][1].body);
|
||
|
|
expect(payload.metadata.openchamber.goal).toMatchObject({
|
||
|
|
objective: 'Finish the migration',
|
||
|
|
objectiveFile: false,
|
||
|
|
});
|
||
|
|
} finally {
|
||
|
|
globalThis.fetch = originalFetch;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it('builds the same goal intro with an optional budget', () => {
|
||
|
|
expect(buildGoalIntroText(null)).toContain('Goal mode is active for this session.');
|
||
|
|
expect(buildGoalIntroText(200_000)).toContain('A token budget of 200000 tokens applies to this goal.');
|
||
|
|
});
|
||
|
|
});
|