tests/testthat/test-hooks.R

test_that("Hooks operate correctly", {
  # Setup mock model
  mock_model <- list(
    provider = "mock",
    do_generate = function(params) {
      list(text = "Mock response")
    },
    format_tool_result = function(id, name, res) {
      list(role = "tool", content = res)
    }
  )
  class(mock_model) <- "LanguageModelV1"
  
  # Test Generation Hooks
  start_called <- FALSE
  end_called <- FALSE
  
  hooks <- create_hooks(
    on_generation_start = function(model, prompt, tools) {
      start_called <<- TRUE
      expect_equal(prompt, "Hello")
    },
    on_generation_end = function(result) {
      end_called <<- TRUE
      expect_equal(result$text, "Mock response")
    }
  )
  
  result <- generate_text(mock_model, "Hello", hooks = hooks)
  
  expect_true(start_called)
  expect_true(end_called)
})

test_that("Tool Hooks operate correctly", {
  # Setup mock tool with a proper schema
  tool_called <- FALSE
  my_tool <- tool("test_tool", "desc", z_object(dummy = z_string()), function(args) {
    tool_called <<- TRUE
    "tool_result"
  })
  
  # Setup hooks
  tool_start_called <- FALSE
  tool_end_called <- FALSE
  
  hooks <- create_hooks(
    on_tool_start = function(t, args) {
      tool_start_called <<- TRUE
      expect_equal(t$name, "test_tool")
    },
    on_tool_end = function(t, res) {
      tool_end_called <<- TRUE
      expect_equal(res, "tool_result") # Raw result
    }
  )
  
  # Execute tool calls manually
  tool_calls <- list(list(id = "1", name = "test_tool", arguments = list()))
  results <- execute_tool_calls(tool_calls, list(my_tool), hooks = hooks)
  
  expect_true(tool_called)
  expect_true(tool_start_called)
  expect_true(tool_end_called)
})

test_that("Permission Hook (Implicit) allows execution", {
  my_tool <- tool("test_tool", "desc", z_object(x = z_string()), function(args) "ok")
  hooks <- create_permission_hook("implicit")
  
  results <- execute_tool_calls(
    list(list(id="1", name="test_tool", arguments=list())),
    list(my_tool),
    hooks = hooks
  )
  
  expect_equal(results[[1]]$result, "ok")
  expect_false(results[[1]]$is_error)
})

test_that("Permission Hook (Escalate) denies in non-interactive", {
  # In test environment, interactive() is FALSE
  my_tool <- tool("dangerous_tool", "desc", z_object(x = z_string()), function(args) "ok")
  hooks <- create_permission_hook("escalate", allowlist = c("safe_tool"))
  
  # Use suppressWarnings since it will warn about non-interactive
  results <- suppressWarnings({
    execute_tool_calls(
      list(list(id="1", name="dangerous_tool", arguments=list())),
      list(my_tool),
      hooks = hooks
    )
  })
  
  # Should have error because on_tool_approval returned FALSE -> stop() -> caught by tryCatch
  expect_true(results[[1]]$is_error)
  expect_match(results[[1]]$result, "Tool execution denied")
})

test_that("Permission Hook (Escalate) allows allowlisted tools", {
  my_tool <- tool("safe_tool", "desc", z_object(x = z_string()), function(args) "safe_result")
  hooks <- create_permission_hook("escalate", allowlist = c("safe_tool"))
  
  results <- execute_tool_calls(
    list(list(id="1", name="safe_tool", arguments=list())),
    list(my_tool),
    hooks = hooks
  )
  
  expect_equal(results[[1]]$result, "safe_result")
  expect_false(results[[1]]$is_error)
})

Try the aisdk package in your browser

Any scripts or data that you put into this service are public.

aisdk documentation built on May 29, 2026, 9:07 a.m.