CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
tools.test.ts236 linesDownload Raw Back to tools
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import { describe, it, expect, vi } from 'vitest';8import type { ToolInvocation, ToolResult } from './tools.js';9import type { PermissionDecision } from '../permissions/types.js';10import { DeclarativeTool, hasCycleInSchema, Kind } from './tools.js';11import { ToolErrorType } from './tool-error.js';12 13class TestToolInvocation implements ToolInvocation<object, ToolResult> {14  constructor(15    readonly params: object,16    private readonly executeFn: () => Promise<ToolResult>,17  ) {}18 19  getDescription(): string {20    return 'A test invocation';21  }22 23  toolLocations() {24    return [];25  }26 27  getDefaultPermission(): Promise<PermissionDecision> {28    return Promise.resolve('allow');29  }30 31  getConfirmationDetails(): Promise<never> {32    throw new Error('Not implemented');33  }34 35  execute(): Promise<ToolResult> {36    return this.executeFn();37  }38}39 40class TestTool extends DeclarativeTool<object, ToolResult> {41  private readonly buildFn: (params: object) => TestToolInvocation;42 43  constructor(buildFn: (params: object) => TestToolInvocation) {44    super('test-tool', 'Test Tool', 'A tool for testing', Kind.Other, {});45    this.buildFn = buildFn;46  }47 48  build(params: object): ToolInvocation<object, ToolResult> {49    return this.buildFn(params);50  }51}52 53describe('DeclarativeTool', () => {54  describe('validateBuildAndExecute', () => {55    const abortSignal = new AbortController().signal;56 57    it('should return INVALID_TOOL_PARAMS error if build fails', async () => {58      const buildError = new Error('Invalid build parameters');59      const buildFn = vi.fn().mockImplementation(() => {60        throw buildError;61      });62      const tool = new TestTool(buildFn);63      const params = { foo: 'bar' };64 65      const result = await tool.validateBuildAndExecute(params, abortSignal);66 67      expect(buildFn).toHaveBeenCalledWith(params);68      expect(result).toEqual({69        llmContent: `Error: Invalid parameters provided. Reason: ${buildError.message}`,70        returnDisplay: buildError.message,71        error: {72          message: buildError.message,73          type: ToolErrorType.INVALID_TOOL_PARAMS,74        },75      });76    });77 78    it('should return EXECUTION_FAILED error if execute fails', async () => {79      const executeError = new Error('Execution failed');80      const executeFn = vi.fn().mockRejectedValue(executeError);81      const invocation = new TestToolInvocation({}, executeFn);82      const buildFn = vi.fn().mockReturnValue(invocation);83      const tool = new TestTool(buildFn);84      const params = { foo: 'bar' };85 86      const result = await tool.validateBuildAndExecute(params, abortSignal);87 88      expect(buildFn).toHaveBeenCalledWith(params);89      expect(executeFn).toHaveBeenCalled();90      expect(result).toEqual({91        llmContent: `Error: Tool call execution failed. Reason: ${executeError.message}`,92        returnDisplay: executeError.message,93        error: {94          message: executeError.message,95          type: ToolErrorType.EXECUTION_FAILED,96        },97      });98    });99 100    it('should return the result of execute on success', async () => {101      const successResult: ToolResult = {102        llmContent: 'Success!',103        returnDisplay: 'Success!',104      };105      const executeFn = vi.fn().mockResolvedValue(successResult);106      const invocation = new TestToolInvocation({}, executeFn);107      const buildFn = vi.fn().mockReturnValue(invocation);108      const tool = new TestTool(buildFn);109      const params = { foo: 'bar' };110 111      const result = await tool.validateBuildAndExecute(params, abortSignal);112 113      expect(buildFn).toHaveBeenCalledWith(params);114      expect(executeFn).toHaveBeenCalled();115      expect(result).toEqual(successResult);116    });117  });118});119 120describe('hasCycleInSchema', () => {121  it('should detect a simple direct cycle', () => {122    const schema = {123      properties: {124        data: {125          $ref: '#/properties/data',126        },127      },128    };129    expect(hasCycleInSchema(schema)).toBe(true);130  });131 132  it('should detect a cycle from object properties referencing parent properties', () => {133    const schema = {134      type: 'object',135      properties: {136        data: {137          type: 'object',138          properties: {139            child: { $ref: '#/properties/data' },140          },141        },142      },143    };144    expect(hasCycleInSchema(schema)).toBe(true);145  });146 147  it('should detect a cycle from array items referencing parent properties', () => {148    const schema = {149      type: 'object',150      properties: {151        data: {152          type: 'array',153          items: {154            type: 'object',155            properties: {156              child: { $ref: '#/properties/data/items' },157            },158          },159        },160      },161    };162    expect(hasCycleInSchema(schema)).toBe(true);163  });164 165  it('should detect a cycle between sibling properties', () => {166    const schema = {167      type: 'object',168      properties: {169        a: {170          type: 'object',171          properties: {172            child: { $ref: '#/properties/b' },173          },174        },175        b: {176          type: 'object',177          properties: {178            child: { $ref: '#/properties/a' },179          },180        },181      },182    };183    expect(hasCycleInSchema(schema)).toBe(true);184  });185 186  it('should not detect a cycle in a valid schema', () => {187    const schema = {188      type: 'object',189      properties: {190        name: { type: 'string' },191        address: { $ref: '#/definitions/address' },192      },193      definitions: {194        address: {195          type: 'object',196          properties: {197            street: { type: 'string' },198            city: { type: 'string' },199          },200        },201      },202    };203    expect(hasCycleInSchema(schema)).toBe(false);204  });205 206  it('should handle non-cyclic sibling refs', () => {207    const schema = {208      properties: {209        a: { $ref: '#/definitions/stringDef' },210        b: { $ref: '#/definitions/stringDef' },211      },212      definitions: {213        stringDef: { type: 'string' },214      },215    };216    expect(hasCycleInSchema(schema)).toBe(false);217  });218 219  it('should handle nested but not cyclic refs', () => {220    const schema = {221      properties: {222        a: { $ref: '#/definitions/defA' },223      },224      definitions: {225        defA: { properties: { b: { $ref: '#/definitions/defB' } } },226        defB: { type: 'string' },227      },228    };229    expect(hasCycleInSchema(schema)).toBe(false);230  });231 232  it('should return false for an empty schema', () => {233    expect(hasCycleInSchema({})).toBe(false);234  });235});236 
basant307/AI_Governance_Project · CoolFace