mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-14 10:18:14 +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
@@ -280,9 +280,10 @@ describe('Cursor Routes Logic', () => {
|
||||
});
|
||||
|
||||
expect(res.status).toBe(404);
|
||||
const json = (await res.json()) as { error?: string };
|
||||
const json = (await res.json()) as { error?: string; reason?: string };
|
||||
expect(typeof json.error).toBe('string');
|
||||
expect(json.error?.length).toBeGreaterThan(0);
|
||||
expect(json.reason).toBe('db_not_found');
|
||||
} finally {
|
||||
if (originalHome !== undefined) {
|
||||
process.env.HOME = originalHome;
|
||||
@@ -344,6 +345,82 @@ describe('Cursor Routes Logic', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('POST /api/cursor/auth/auto-detect returns 503 when sqlite3 is unavailable', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalHome = process.env.HOME;
|
||||
const originalPath = process.env.PATH;
|
||||
const originalSqliteBin = process.env.CCS_CURSOR_SQLITE_BIN;
|
||||
const fakeHome = path.join(tempDir, 'sqlite-missing-home');
|
||||
process.env.HOME = fakeHome;
|
||||
process.env.CCS_CURSOR_SQLITE_BIN = 'definitely-missing-sqlite3';
|
||||
|
||||
try {
|
||||
const dbPath = getTokenStoragePath();
|
||||
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
||||
fs.writeFileSync(dbPath, '');
|
||||
|
||||
const res = await fetch(`${baseUrl}/api/cursor/auth/auto-detect`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(503);
|
||||
const json = (await res.json()) as { reason?: string; db_path?: string };
|
||||
expect(json.reason).toBe('sqlite_unavailable');
|
||||
expect(json.db_path).toBeUndefined();
|
||||
} finally {
|
||||
if (originalHome !== undefined) {
|
||||
process.env.HOME = originalHome;
|
||||
} else {
|
||||
delete process.env.HOME;
|
||||
}
|
||||
|
||||
if (originalPath !== undefined) {
|
||||
process.env.PATH = originalPath;
|
||||
} else {
|
||||
delete process.env.PATH;
|
||||
}
|
||||
if (originalSqliteBin !== undefined) {
|
||||
process.env.CCS_CURSOR_SQLITE_BIN = originalSqliteBin;
|
||||
} else {
|
||||
delete process.env.CCS_CURSOR_SQLITE_BIN;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('POST /api/cursor/auth/auto-detect returns 500 when the database exists but cannot be queried', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalHome = process.env.HOME;
|
||||
const fakeHome = path.join(tempDir, 'query-failed-home');
|
||||
process.env.HOME = fakeHome;
|
||||
|
||||
try {
|
||||
const dbPath = getTokenStoragePath();
|
||||
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
||||
fs.writeFileSync(dbPath, 'not-a-sqlite-database');
|
||||
|
||||
const res = await fetch(`${baseUrl}/api/cursor/auth/auto-detect`, {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(500);
|
||||
const json = (await res.json()) as { reason?: string; db_path?: string };
|
||||
expect(json.reason).toBe('db_query_failed');
|
||||
expect(json.db_path).toBeUndefined();
|
||||
} finally {
|
||||
if (originalHome !== undefined) {
|
||||
process.env.HOME = originalHome;
|
||||
} else {
|
||||
delete process.env.HOME;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('POST /api/cursor/daemon/start returns 400 when integration is disabled', async () => {
|
||||
seedCursorConfig({ enabled: false });
|
||||
seedCredentials(false);
|
||||
@@ -412,5 +489,28 @@ describe('Cursor Routes Logic', () => {
|
||||
expect(json.models.length).toBeGreaterThan(0);
|
||||
expect(json.current).toBe('gpt-5.3-codex');
|
||||
});
|
||||
|
||||
it('POST /api/cursor/probe returns auth failure when credentials are missing', async () => {
|
||||
const res = await fetch(`${baseUrl}/api/cursor/probe`, { method: 'POST' });
|
||||
expect(res.status).toBe(401);
|
||||
|
||||
const json = (await res.json()) as { ok?: boolean; stage?: string; error_type?: string };
|
||||
expect(json.ok).toBe(false);
|
||||
expect(json.stage).toBe('auth');
|
||||
expect(json.error_type).toBe('authentication_error');
|
||||
});
|
||||
|
||||
it('POST /api/cursor/probe returns daemon failure when daemon is down and auto_start is false', async () => {
|
||||
seedCursorConfig({ enabled: true, auto_start: false });
|
||||
seedCredentials(false);
|
||||
|
||||
const res = await fetch(`${baseUrl}/api/cursor/probe`, { method: 'POST' });
|
||||
expect(res.status).toBe(503);
|
||||
|
||||
const json = (await res.json()) as { ok?: boolean; stage?: string; error_type?: string };
|
||||
expect(json.ok).toBe(false);
|
||||
expect(json.stage).toBe('daemon');
|
||||
expect(json.error_type).toBe('daemon_not_running');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user