From 7e75bfd8121d4077e0d773fa44735ce7d437ad9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Thu=E1=BA=ADn=20Ph=C3=A1t?= Date: Tue, 17 Mar 2026 00:02:52 +0700 Subject: [PATCH 1/2] fix: skip env var case-sensitivity test on Windows Windows treats environment variable names as case-insensitive, so setting both test_var and TEST_VAR overwrites the same variable. Skip this test on Windows since the behavior is platform-dependent by design, not a code bug. Also removes leftover console.log debug line. Fixes #229 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../brain/memAgent/__test__/loader.test.ts | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/core/brain/memAgent/__test__/loader.test.ts b/src/core/brain/memAgent/__test__/loader.test.ts index b6e41586..abb6e3f7 100644 --- a/src/core/brain/memAgent/__test__/loader.test.ts +++ b/src/core/brain/memAgent/__test__/loader.test.ts @@ -255,27 +255,32 @@ describe('Loader', () => { expect(result.mcpServers.testServer.args).toEqual([1, 2, 3]); }); - it('should handle case-insensitive environment variable names', async () => { - process.env.test_var = 'lowercase'; - process.env.TEST_VAR = 'uppercase'; - - const mockConfig = { - systemPrompt: '$test_var', - llm: { - provider: 'openai', - model: 'gpt-4', - apiKey: '$TEST_VAR', - }, - }; + // Windows treats environment variable names as case-insensitive, + // so setting both test_var and TEST_VAR overwrites the same variable. + // This test only applies to platforms with case-sensitive env vars. + it.skipIf(process.platform === 'win32')( + 'should handle case-insensitive environment variable names', + async () => { + process.env.test_var = 'lowercase'; + process.env.TEST_VAR = 'uppercase'; + + const mockConfig = { + systemPrompt: '$test_var', + llm: { + provider: 'openai', + model: 'gpt-4', + apiKey: '$TEST_VAR', + }, + }; - mockFs.readFile.mockResolvedValue(JSON.stringify(mockConfig)); - mockParseYaml.mockReturnValue(mockConfig); + mockFs.readFile.mockResolvedValue(JSON.stringify(mockConfig)); + mockParseYaml.mockReturnValue(mockConfig); - const result = (await loadAgentConfig('/path/to/config.yml')) as any; - console.log(result); - expect(result.systemPrompt).toBe('lowercase'); - expect(result.llm.apiKey).toBe('uppercase'); - }); + const result = (await loadAgentConfig('/path/to/config.yml')) as any; + expect(result.systemPrompt).toBe('lowercase'); + expect(result.llm.apiKey).toBe('uppercase'); + } + ); }); describe('Config File Loading', () => { From d3e810217ed245781b7fe10e19adeaa150120476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Thu=E1=BA=ADn=20Ph=C3=A1t?= Date: Tue, 17 Mar 2026 00:05:00 +0700 Subject: [PATCH 2/2] style: fix prettier formatting in neo4j.ts Pre-existing formatting issue unrelated to the test fix. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/core/knowledge_graph/backend/neo4j.ts | 126 +++++++++++----------- 1 file changed, 62 insertions(+), 64 deletions(-) diff --git a/src/core/knowledge_graph/backend/neo4j.ts b/src/core/knowledge_graph/backend/neo4j.ts index 671bdbd7..5bb858d4 100644 --- a/src/core/knowledge_graph/backend/neo4j.ts +++ b/src/core/knowledge_graph/backend/neo4j.ts @@ -931,70 +931,68 @@ export class Neo4jBackend implements KnowledgeGraph { return converted; } - private buildFilterConstraints( - filters: NodeFilters | EdgeFilters, - prefix: string, - params: any - ): string { - const constraints: string[] = []; - - for (const [rawKey, filter] of Object.entries(filters)) { - const propertyKey = this.escapePropertyKey(rawKey); - const paramKey = `${prefix}_${Object.keys(params).length}`; - - if (typeof filter === 'object' && filter !== null && !Array.isArray(filter)) { - // Range filters - if ('gte' in filter) { - constraints.push(`${prefix}.${propertyKey} >= $${paramKey}_gte`); - params[`${paramKey}_gte`] = filter.gte; - } - if ('gt' in filter) { - constraints.push(`${prefix}.${propertyKey} > $${paramKey}_gt`); - params[`${paramKey}_gt`] = filter.gt; - } - if ('lte' in filter) { - constraints.push(`${prefix}.${propertyKey} <= $${paramKey}_lte`); - params[`${paramKey}_lte`] = filter.lte; - } - if ('lt' in filter) { - constraints.push(`${prefix}.${propertyKey} < $${paramKey}_lt`); - params[`${paramKey}_lt`] = filter.lt; - } - - // Array filters - if ('any' in filter && Array.isArray(filter.any)) { - constraints.push(`${prefix}.${propertyKey} IN $${paramKey}`); - params[paramKey] = filter.any; - } - if ('all' in filter && Array.isArray(filter.all)) { - // For 'all' filter, check if the property (assumed to be array) contains all values - constraints.push( - `all(x IN $${paramKey} WHERE x IN ${prefix}.${propertyKey})` - ); - params[paramKey] = filter.all; - } - } else { - // Direct equality - constraints.push(`${prefix}.${propertyKey} = $${paramKey}`); - params[paramKey] = filter; - } - } - - return constraints.join(' AND '); - } - - private escapePropertyKey(key: string): string { - if (key.trim().length === 0) { - throw new Error('Filter property names must not be empty.'); - } - - if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) { - return key; - } - - const escapedKey = key.replace(/`/g, '``'); - return `\`${escapedKey}\``; - } + private buildFilterConstraints( + filters: NodeFilters | EdgeFilters, + prefix: string, + params: any + ): string { + const constraints: string[] = []; + + for (const [rawKey, filter] of Object.entries(filters)) { + const propertyKey = this.escapePropertyKey(rawKey); + const paramKey = `${prefix}_${Object.keys(params).length}`; + + if (typeof filter === 'object' && filter !== null && !Array.isArray(filter)) { + // Range filters + if ('gte' in filter) { + constraints.push(`${prefix}.${propertyKey} >= $${paramKey}_gte`); + params[`${paramKey}_gte`] = filter.gte; + } + if ('gt' in filter) { + constraints.push(`${prefix}.${propertyKey} > $${paramKey}_gt`); + params[`${paramKey}_gt`] = filter.gt; + } + if ('lte' in filter) { + constraints.push(`${prefix}.${propertyKey} <= $${paramKey}_lte`); + params[`${paramKey}_lte`] = filter.lte; + } + if ('lt' in filter) { + constraints.push(`${prefix}.${propertyKey} < $${paramKey}_lt`); + params[`${paramKey}_lt`] = filter.lt; + } + + // Array filters + if ('any' in filter && Array.isArray(filter.any)) { + constraints.push(`${prefix}.${propertyKey} IN $${paramKey}`); + params[paramKey] = filter.any; + } + if ('all' in filter && Array.isArray(filter.all)) { + // For 'all' filter, check if the property (assumed to be array) contains all values + constraints.push(`all(x IN $${paramKey} WHERE x IN ${prefix}.${propertyKey})`); + params[paramKey] = filter.all; + } + } else { + // Direct equality + constraints.push(`${prefix}.${propertyKey} = $${paramKey}`); + params[paramKey] = filter; + } + } + + return constraints.join(' AND '); + } + + private escapePropertyKey(key: string): string { + if (key.trim().length === 0) { + throw new Error('Filter property names must not be empty.'); + } + + if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) { + return key; + } + + const escapedKey = key.replace(/`/g, '``'); + return `\`${escapedKey}\``; + } private async createIndexes(): Promise { const session = this.getSession();