tests/testthat/test-skill-wrapper.R

test_that("skill_wrapper creates a closure with correct class", {
  skill_dir <- system.file(
    "builtin-templates/bslib-bare/agents/skills/greet",
    package = "shidashi"
  )
  skip_if(!nzchar(skill_dir), "greet skill not installed")

  wrapper <- skill_wrapper(skill_dir)
  expect_s3_class(wrapper, "shidashi_skill_wrapper")
  expect_true(is.function(wrapper))
})

test_that("skill_wrapper errors on missing SKILL.md", {
  tmp <- tempfile("empty_skill")
  dir.create(tmp)
  on.exit(unlink(tmp, recursive = TRUE), add = TRUE)

  expect_error(skill_wrapper(tmp), "SKILL.md not found")
})

test_that("wrapper() returns an ellmer::ToolDef", {
  skill_dir <- system.file(
    "builtin-templates/bslib-bare/agents/skills/greet",
    package = "shidashi"
  )
  skip_if(!nzchar(skill_dir), "greet skill not installed")

  wrapper <- skill_wrapper(skill_dir)
  tool_def <- wrapper()
  expect_true(inherits(tool_def, "ellmer::ToolDef"))
  expect_equal(tool_def@name, "greet")
})

test_that("action='readme' returns SKILL.md body with script listing", {
  skill_dir <- system.file(
    "builtin-templates/bslib-bare/agents/skills/greet",
    package = "shidashi"
  )
  skip_if(!nzchar(skill_dir), "greet skill not installed")

  tool_def <- skill_wrapper(skill_dir)()
  result <- tool_def(action = "readme")
  expect_type(result, "character")
  expect_match(result, "Instructions")
  expect_match(result, "greet\\.R")
})

test_that("soft gate augments errors before readme", {
  skill_dir <- system.file(
    "builtin-templates/bslib-bare/agents/skills/greet",
    package = "shidashi"
  )
  skip_if(!nzchar(skill_dir), "greet skill not installed")

  tool_def <- skill_wrapper(skill_dir)()

  # Requesting a nonexistent reference triggers error + readme hint
  err <- tryCatch(
    tool_def(action = "script", file_name = "nonexistent.R"),
    error = function(e) conditionMessage(e)
  )
  expect_match(err, "action='readme' first", fixed = TRUE)
})

test_that("soft gate allows successful calls before readme", {
  skip_if(!requireNamespace("processx", quietly = TRUE), "processx not installed")
  skill_dir <- system.file(
    "builtin-templates/bslib-bare/agents/skills/greet",
    package = "shidashi"
  )
  skip_if(!nzchar(skill_dir), "greet skill not installed")

  tool_def <- skill_wrapper(skill_dir)()

  # Valid script call should succeed even without readme

  result <- tool_def(action = "script", file_name = "greet.R",
                     args = list("TestUser"))
  expect_match(result, "Hello, TestUser!")
})

test_that("after readme, errors are not augmented", {
  skill_dir <- system.file(
    "builtin-templates/bslib-bare/agents/skills/greet",
    package = "shidashi"
  )
  skip_if(!nzchar(skill_dir), "greet skill not installed")

  tool_def <- skill_wrapper(skill_dir)()
  tool_def(action = "readme")  # unlock

  err <- tryCatch(
    tool_def(action = "script", file_name = "nonexistent.R"),
    error = function(e) conditionMessage(e)
  )
  expect_match(err, "Script not found")
  expect_false(grepl("action='readme' first", err, fixed = TRUE))
})

test_that("action='script' runs greet.R via processx", {
  skip_if(!requireNamespace("processx", quietly = TRUE), "processx not installed")
  skill_dir <- system.file(
    "builtin-templates/bslib-bare/agents/skills/greet",
    package = "shidashi"
  )
  skip_if(!nzchar(skill_dir), "greet skill not installed")

  tool_def <- skill_wrapper(skill_dir)()
  tool_def(action = "readme")  # unlock
  result <- tool_def(action = "script", file_name = "greet.R",
                     args = list("Copilot"))
  expect_match(result, "Hello, Copilot!")
  expect_match(result, "Exit code: 0", fixed = TRUE)
})

test_that("each wrapper() call gets independent gate state", {
  skill_dir <- system.file(
    "builtin-templates/bslib-bare/agents/skills/greet",
    package = "shidashi"
  )
  skip_if(!nzchar(skill_dir), "greet skill not installed")

  wrapper <- skill_wrapper(skill_dir)
  tool1 <- wrapper()
  tool2 <- wrapper()

  # Unlock tool1

  tool1(action = "readme")

  # tool2 should still have gate active (error augmented on failure)
  err <- tryCatch(
    tool2(action = "script", file_name = "nonexistent.R"),
    error = function(e) conditionMessage(e)
  )
  expect_match(err, "action='readme' first", fixed = TRUE)

  # tool1 gate is unlocked — error not augmented
  err1 <- tryCatch(
    tool1(action = "script", file_name = "nonexistent.R"),
    error = function(e) conditionMessage(e)
  )
  expect_false(grepl("action='readme' first", err1, fixed = TRUE))
})

Try the shidashi package in your browser

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

shidashi documentation built on April 10, 2026, 5:07 p.m.