| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- import { safeSql } from '@supabase/pg-meta'
- import { beforeEach, describe, expect, it, vi } from 'vitest'
- import { getStudioTools } from './studio-tools'
- import { executeSql } from '@/data/sql/execute-sql-query'
- import { NO_DATA_PERMISSIONS } from '@/lib/ai/tools/tool-sanitizer'
- vi.mock('@/data/sql/execute-sql-query', () => ({
- executeSql: vi.fn(),
- }))
- describe('ai/tools/studio-tools', () => {
- beforeEach(() => {
- vi.mocked(executeSql).mockReset()
- })
- describe('getStudioTools', () => {
- it('should return an object with tool definitions', () => {
- const tools = getStudioTools()
- expect(tools).toBeDefined()
- expect(typeof tools).toBe('object')
- })
- it('should include execute_sql tool', () => {
- const tools = getStudioTools()
- expect(tools.execute_sql).toBeDefined()
- expect(tools.execute_sql.description).toContain('execute a SQL statement')
- })
- it('should include deploy_edge_function tool', () => {
- const tools = getStudioTools()
- expect(tools.deploy_edge_function).toBeDefined()
- expect(tools.deploy_edge_function.description).toContain('deploy a Briven Edge Function')
- })
- it('should include rename_chat tool', () => {
- const tools = getStudioTools()
- expect(tools.rename_chat).toBeDefined()
- expect(tools.rename_chat.description).toContain('Rename the current chat session')
- })
- it('should have exactly 4 tools', () => {
- const tools = getStudioTools()
- const toolNames = Object.keys(tools)
- expect(toolNames).toHaveLength(4)
- expect(toolNames).toContain('load_knowledge')
- expect(toolNames).toContain('execute_sql')
- expect(toolNames).toContain('deploy_edge_function')
- expect(toolNames).toContain('rename_chat')
- })
- it('should have execute_sql with correct input schema fields', () => {
- const tools = getStudioTools()
- const executeSqlTool = tools.execute_sql
- // Check that the tool has an input schema
- expect(executeSqlTool.inputSchema).toBeDefined()
- // Verify the schema exists and is a Zod object
- const schema = executeSqlTool.inputSchema
- expect(schema).toBeDefined()
- expect((schema as any)._def.typeName).toBe('ZodObject')
- })
- it('should have deploy_edge_function with input schema', () => {
- const tools = getStudioTools()
- const deployTool = tools.deploy_edge_function
- expect(deployTool.inputSchema).toBeDefined()
- // Verify the schema exists and is a Zod object
- expect(deployTool.inputSchema).toBeDefined()
- expect((deployTool.inputSchema as any)._def.typeName).toBe('ZodObject')
- })
- it('should have rename_chat with execute function', async () => {
- const tools = getStudioTools()
- const renameTool = tools.rename_chat
- expect(renameTool.execute).toBeDefined()
- expect(typeof renameTool.execute).toBe('function')
- // Test the execute function
- if (!renameTool.execute) throw new Error('execute is undefined')
- const result = await renameTool.execute(
- { newName: 'Test Chat' },
- { toolCallId: 'test', messages: [] }
- )
- expect(result).toEqual({ status: 'Chat request sent to client' })
- })
- it('should validate execute_sql input schema correctly', () => {
- const tools = getStudioTools()
- const schema = tools.execute_sql.inputSchema
- // Check if schema is a Zod schema with safeParse
- if ('safeParse' in schema) {
- // Valid input
- const validInput = {
- sql: safeSql`SELECT * FROM users`,
- label: 'Get users',
- chartConfig: { view: 'table' as const },
- isWriteQuery: false,
- }
- expect(schema.safeParse(validInput).success).toBe(true)
- // Valid chart config
- const validChartInput = {
- sql: safeSql`SELECT count(*) FROM users`,
- label: 'User count',
- chartConfig: { view: 'chart' as const, xAxis: 'date', yAxis: 'count' },
- isWriteQuery: false,
- }
- expect(schema.safeParse(validChartInput).success).toBe(true)
- // Missing required field
- const invalidInput = {
- sql: safeSql`SELECT * FROM users`,
- // missing label, chartConfig, isWriteQuery
- }
- expect(schema.safeParse(invalidInput).success).toBe(false)
- } else {
- // Skip test if schema doesn't have safeParse
- expect(schema).toBeDefined()
- }
- })
- it('should require approval for read and write SQL queries', () => {
- const tools = getStudioTools()
- expect(tools.execute_sql.needsApproval).toBe(true)
- })
- it('should return execute_sql rows to the UI and sanitize model output without data opt-in', async () => {
- const rows = [{ email: 'test@example.com' }]
- vi.mocked(executeSql).mockResolvedValue({ result: rows })
- const tools = getStudioTools({
- projectRef: 'test-project',
- connectionString: 'encrypted-connection-string',
- aiOptInLevel: 'schema',
- })
- if (!tools.execute_sql.execute) throw new Error('execute is undefined')
- const result = await tools.execute_sql.execute(
- {
- sql: 'SELECT email FROM users',
- label: 'Get emails',
- chartConfig: { view: 'table' },
- isWriteQuery: false,
- },
- { toolCallId: 'test', messages: [] }
- )
- expect(executeSql).toHaveBeenCalledWith(
- {
- projectRef: 'test-project',
- connectionString: 'encrypted-connection-string',
- sql: 'SELECT email FROM users',
- },
- undefined,
- undefined
- )
- expect(result).toEqual(rows)
- expect((tools.execute_sql as any).toModelOutput({ output: result })).toEqual({
- type: 'text',
- value: NO_DATA_PERMISSIONS,
- })
- })
- it('should return execute_sql rows with data opt-in', async () => {
- const rows = [{ email: 'test@example.com' }]
- vi.mocked(executeSql).mockResolvedValue({ result: rows })
- const tools = getStudioTools({
- projectRef: 'test-project',
- connectionString: 'encrypted-connection-string',
- aiOptInLevel: 'schema_and_log_and_data',
- })
- if (!tools.execute_sql.execute) throw new Error('execute is undefined')
- const result = await tools.execute_sql.execute(
- {
- sql: 'SELECT email FROM users',
- label: 'Get emails',
- chartConfig: { view: 'table' },
- isWriteQuery: false,
- },
- { toolCallId: 'test', messages: [] }
- )
- expect(executeSql).toHaveBeenCalledWith(
- {
- projectRef: 'test-project',
- connectionString: 'encrypted-connection-string',
- sql: 'SELECT email FROM users',
- },
- undefined,
- undefined
- )
- expect(result).toEqual(rows)
- expect((tools.execute_sql as any).toModelOutput({ output: result })).toEqual({
- type: 'json',
- value: rows,
- })
- })
- it('should validate rename_chat input schema correctly', () => {
- const tools = getStudioTools()
- const schema = tools.rename_chat.inputSchema
- // Check if schema is a Zod schema with safeParse
- if ('safeParse' in schema) {
- // Valid input
- expect(schema.safeParse({ newName: 'My Chat' }).success).toBe(true)
- // Invalid input - missing newName
- expect(schema.safeParse({}).success).toBe(false)
- // Invalid input - wrong type
- expect(schema.safeParse({ newName: 123 }).success).toBe(false)
- } else {
- // Skip test if schema doesn't have safeParse
- expect(schema).toBeDefined()
- }
- })
- })
- })
|