diff options
| author | Jacob MacDonald <[email protected]> | 2025-08-05 18:48:00 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-08-06 01:48:00 +0000 |
| commit | 7e5a5e2da79783554dc4e3f00787317db29a589a (patch) | |
| tree | 065c53a2037fd6b9f42ba32f00bc5a576a8f1cfa /packages/core/src/tools/tools.test.ts | |
| parent | 9db5aab4987ad64317a2f6724880265f8124250d (diff) | |
Detect and warn about cyclic tool refs when schema depth errors are encountered (#5609)
Co-authored-by: Jacob Richman <[email protected]>
Diffstat (limited to 'packages/core/src/tools/tools.test.ts')
| -rw-r--r-- | packages/core/src/tools/tools.test.ts | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/packages/core/src/tools/tools.test.ts b/packages/core/src/tools/tools.test.ts new file mode 100644 index 00000000..9942d3a9 --- /dev/null +++ b/packages/core/src/tools/tools.test.ts @@ -0,0 +1,125 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, it, expect } from 'vitest'; +import { hasCycleInSchema } from './tools.js'; // Added getStringifiedResultForDisplay + +describe('hasCycleInSchema', () => { + it('should detect a simple direct cycle', () => { + const schema = { + properties: { + data: { + $ref: '#/properties/data', + }, + }, + }; + expect(hasCycleInSchema(schema)).toBe(true); + }); + + it('should detect a cycle from object properties referencing parent properties', () => { + const schema = { + type: 'object', + properties: { + data: { + type: 'object', + properties: { + child: { $ref: '#/properties/data' }, + }, + }, + }, + }; + expect(hasCycleInSchema(schema)).toBe(true); + }); + + it('should detect a cycle from array items referencing parent properties', () => { + const schema = { + type: 'object', + properties: { + data: { + type: 'array', + items: { + type: 'object', + properties: { + child: { $ref: '#/properties/data/items' }, + }, + }, + }, + }, + }; + expect(hasCycleInSchema(schema)).toBe(true); + }); + + it('should detect a cycle between sibling properties', () => { + const schema = { + type: 'object', + properties: { + a: { + type: 'object', + properties: { + child: { $ref: '#/properties/b' }, + }, + }, + b: { + type: 'object', + properties: { + child: { $ref: '#/properties/a' }, + }, + }, + }, + }; + expect(hasCycleInSchema(schema)).toBe(true); + }); + + it('should not detect a cycle in a valid schema', () => { + const schema = { + type: 'object', + properties: { + name: { type: 'string' }, + address: { $ref: '#/definitions/address' }, + }, + definitions: { + address: { + type: 'object', + properties: { + street: { type: 'string' }, + city: { type: 'string' }, + }, + }, + }, + }; + expect(hasCycleInSchema(schema)).toBe(false); + }); + + it('should handle non-cyclic sibling refs', () => { + const schema = { + properties: { + a: { $ref: '#/definitions/stringDef' }, + b: { $ref: '#/definitions/stringDef' }, + }, + definitions: { + stringDef: { type: 'string' }, + }, + }; + expect(hasCycleInSchema(schema)).toBe(false); + }); + + it('should handle nested but not cyclic refs', () => { + const schema = { + properties: { + a: { $ref: '#/definitions/defA' }, + }, + definitions: { + defA: { properties: { b: { $ref: '#/definitions/defB' } } }, + defB: { type: 'string' }, + }, + }; + expect(hasCycleInSchema(schema)).toBe(false); + }); + + it('should return false for an empty schema', () => { + expect(hasCycleInSchema({})).toBe(false); + }); +}); |
