From c35fc27427ae8cce10ad2b0ac03ec954812eff5f Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 14 Jul 2026 03:07:33 +0700 Subject: [PATCH] fix(cli): fail cypher errors loudly --- gitnexus/src/cli/tool.ts | 9 +++++++++ gitnexus/test/unit/tool-direct-cli.test.ts | 23 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/gitnexus/src/cli/tool.ts b/gitnexus/src/cli/tool.ts index 688c66535..46adaf990 100644 --- a/gitnexus/src/cli/tool.ts +++ b/gitnexus/src/cli/tool.ts @@ -301,6 +301,15 @@ export async function cypherCommand( } } output(result); + if ( + result && + typeof result === 'object' && + 'error' in result && + typeof result.error === 'string' && + result.error.trim().length > 0 + ) { + process.exitCode = 1; + } } export async function detectChangesCommand(options?: { diff --git a/gitnexus/test/unit/tool-direct-cli.test.ts b/gitnexus/test/unit/tool-direct-cli.test.ts index 62e5fa1ea..2230a675e 100644 --- a/gitnexus/test/unit/tool-direct-cli.test.ts +++ b/gitnexus/test/unit/tool-direct-cli.test.ts @@ -81,6 +81,29 @@ describe('direct CLI tool commands', () => { expect(process.exitCode).toBe(1); }); + it('fails closed when cypher returns a backend error payload', async () => { + callToolMock.mockResolvedValue({ error: 'Binder exception: missing relationship property' }); + const { cypherCommand } = await import('../../src/cli/tool.js'); + + await cypherCommand('MATCH ()-[r:CodeRelation]->() RETURN r.missing'); + + expect(writeSyncMock).toHaveBeenCalledWith( + 1, + expect.stringContaining('Binder exception: missing relationship property'), + ); + expect(process.exitCode).toBe(1); + }); + + it('keeps a successful cypher result at exit zero', async () => { + callToolMock.mockResolvedValue({ markdown: '| count |\n| --- |\n| 1 |', row_count: 1 }); + const { cypherCommand } = await import('../../src/cli/tool.js'); + + await cypherCommand('MATCH (n) RETURN count(n) AS count'); + + expect(writeSyncMock).toHaveBeenCalledWith(1, expect.stringContaining('"row_count": 1')); + expect(process.exitCode).toBeUndefined(); + }); + it('dispatches detect_changes with CLI-shaped arguments', async () => { callToolMock.mockResolvedValue({ summary: {