summaryrefslogtreecommitdiff
path: root/packages/core/src/tools/tools.test.ts
diff options
context:
space:
mode:
authorJacob MacDonald <[email protected]>2025-08-05 18:48:00 -0700
committerGitHub <[email protected]>2025-08-06 01:48:00 +0000
commit7e5a5e2da79783554dc4e3f00787317db29a589a (patch)
tree065c53a2037fd6b9f42ba32f00bc5a576a8f1cfa /packages/core/src/tools/tools.test.ts
parent9db5aab4987ad64317a2f6724880265f8124250d (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.ts125
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);
+ });
+});