mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-20 00:14:58 +00:00
feat(cursor): harden live probe and runtime contracts
This commit is contained in:
1 parent
2eaf760d20
commit
e1049e38d4
23 files changed
+2186
-306
No files matched your search
@@ -0,0 +1,116 @@
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { ReactNode } from 'react';
|
||||
import { AllProviders } from '../../setup/test-utils';
|
||||
import { useCursor } from '@/hooks/use-cursor';
|
||||
|
||||
function createJsonResponse(body: Record<string, unknown>, status = 200): Response {
|
||||
return new Response(JSON.stringify(body), {
|
||||
status,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) => <AllProviders>{children}</AllProviders>;
|
||||
|
||||
describe('useCursor', () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('preserves structured probe failures and refreshes live status queries', async () => {
|
||||
const probeFailure = {
|
||||
ok: false,
|
||||
stage: 'daemon',
|
||||
status: 503,
|
||||
duration_ms: 321,
|
||||
error_type: 'daemon_not_running',
|
||||
message: 'Cursor daemon is not running.',
|
||||
};
|
||||
|
||||
const fetchMock = vi.fn((input: RequestInfo | URL, init?: RequestInit) => {
|
||||
const url = String(input);
|
||||
|
||||
if (url.endsWith('/api/cursor/status')) {
|
||||
return Promise.resolve(
|
||||
createJsonResponse({
|
||||
enabled: true,
|
||||
authenticated: true,
|
||||
auth_method: 'manual',
|
||||
token_age: 1,
|
||||
token_expired: false,
|
||||
daemon_running: false,
|
||||
port: 20129,
|
||||
auto_start: false,
|
||||
ghost_mode: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (url.endsWith('/api/cursor/settings')) {
|
||||
return Promise.resolve(
|
||||
createJsonResponse({
|
||||
enabled: true,
|
||||
port: 20129,
|
||||
auto_start: false,
|
||||
ghost_mode: true,
|
||||
model: 'gpt-5.3-codex',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (url.endsWith('/api/cursor/models')) {
|
||||
return Promise.resolve(
|
||||
createJsonResponse({
|
||||
models: [{ id: 'gpt-5.3-codex', name: 'GPT-5.3 Codex', provider: 'openai' }],
|
||||
current: 'gpt-5.3-codex',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (url.endsWith('/api/cursor/settings/raw')) {
|
||||
return Promise.resolve(
|
||||
createJsonResponse({
|
||||
settings: {},
|
||||
mtime: 100,
|
||||
path: '/tmp/cursor.settings.json',
|
||||
exists: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (url.endsWith('/api/cursor/probe') && init?.method === 'POST') {
|
||||
return Promise.resolve(createJsonResponse(probeFailure, 503));
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(`Unexpected fetch: ${url}`));
|
||||
});
|
||||
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
const { result } = renderHook(() => useCursor(), { wrapper });
|
||||
|
||||
await waitFor(() => expect(result.current.status?.enabled).toBe(true));
|
||||
await waitFor(() => expect(result.current.models.length).toBe(1));
|
||||
|
||||
await act(async () => {
|
||||
const probeResult = await result.current.runProbeAsync();
|
||||
expect(probeResult).toMatchObject(probeFailure);
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.probeResult).toMatchObject(probeFailure));
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
fetchMock.mock.calls.filter(([input]) => String(input).endsWith('/api/cursor/status'))
|
||||
.length
|
||||
).toBeGreaterThanOrEqual(2)
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
fetchMock.mock.calls.filter(([input]) => String(input).endsWith('/api/cursor/models'))
|
||||
.length
|
||||
).toBeGreaterThanOrEqual(2)
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user