# Tests for Skill class library(testthat) library(aisdk) # Get path to test fixtures fixtures_path <- testthat::test_path("fixtures", "skills") test_skill_path <- file.path(fixtures_path, "test_skill") # === Tests for Skill initialization === test_that("Skill can be created from valid SKILL.md", { skip_if_not(dir.exists(test_skill_path), "Test fixtures not found") skill <- Skill$new(test_skill_path) expect_s3_class(skill, "Skill") expect_equal(skill$name, "test_skill") expect_equal(skill$description, "A test skill for unit testing the Skills system") }) test_that("Skill parses aliases from YAML frontmatter", { tmp_dir <- tempfile() dir.create(tmp_dir) on.exit(unlink(tmp_dir, recursive = TRUE)) writeLines(c( "---", "name: alias_skill", "description: skill with aliases", "aliases:", " - foo", " - bar", "---", "Body" ), file.path(tmp_dir, "SKILL.md")) skill <- Skill$new(tmp_dir) expect_equal(skill$aliases, c("foo", "bar")) }) test_that("Skill parses when_to_use and paths from YAML frontmatter", { tmp_dir <- tempfile() dir.create(tmp_dir) on.exit(unlink(tmp_dir, recursive = TRUE)) writeLines(c( "---", "name: routed_skill", "description: skill with routing metadata", "when_to_use: Use this when the user mentions RNA-seq or differential expression", "paths:", " - data/*.csv", " - analysis/**/*.R", "---", "Body" ), file.path(tmp_dir, "SKILL.md")) skill <- Skill$new(tmp_dir) expect_equal(skill$when_to_use, "Use this when the user mentions RNA-seq or differential expression") expect_equal(skill$paths, c("data/*.csv", "analysis/**/*.R")) }) test_that("Skill matches paths using glob-like patterns", { tmp_dir <- tempfile() dir.create(tmp_dir) on.exit(unlink(tmp_dir, recursive = TRUE)) writeLines(c( "---", "name: path_skill", "description: path matching skill", "paths:", " - scripts/*.R", "---", "Body" ), file.path(tmp_dir, "SKILL.md")) skill <- Skill$new(tmp_dir) expect_true(skill$matches_paths(c("scripts/run.R"), cwd = tmp_dir)) expect_false(skill$matches_paths(c("data/run.R"), cwd = tmp_dir)) }) test_that("Skill$path is normalized", { skip_if_not(dir.exists(test_skill_path), "Test fixtures not found") skill <- Skill$new(test_skill_path) expect_true(file.exists(skill$path)) expect_equal(skill$path, normalizePath(test_skill_path, winslash = "/", mustWork = TRUE)) }) test_that("Skill validates path argument", { expect_error(Skill$new(123), "character string") expect_error(Skill$new(c("a", "b")), "character string") }) test_that("Skill aborts if SKILL.md not found", { tmp_dir <- tempfile() dir.create(tmp_dir) on.exit(unlink(tmp_dir, recursive = TRUE)) expect_error(Skill$new(tmp_dir), "SKILL.md not found") }) test_that("Skill aborts if YAML frontmatter missing", { tmp_dir <- tempfile() dir.create(tmp_dir) on.exit(unlink(tmp_dir, recursive = TRUE)) writeLines("# No frontmatter here", file.path(tmp_dir, "SKILL.md")) expect_error(Skill$new(tmp_dir), "YAML frontmatter") }) test_that("Skill aborts if name field missing", { tmp_dir <- tempfile() dir.create(tmp_dir) on.exit(unlink(tmp_dir, recursive = TRUE)) writeLines(c("---", "description: test", "---"), file.path(tmp_dir, "SKILL.md")) expect_error(Skill$new(tmp_dir), "name") }) # === Tests for Skill$load === test_that("Skill$load returns SKILL.md body content", { skip_if_not(dir.exists(test_skill_path), "Test fixtures not found") skill <- Skill$new(test_skill_path) body <- skill$load() expect_type(body, "character") expect_true(grepl("Test Skill Instructions", body)) expect_true(grepl("Available Scripts", body)) }) # === Tests for Skill$list_scripts === test_that("Skill$list_scripts returns R files", { skip_if_not(dir.exists(test_skill_path), "Test fixtures not found") skill <- Skill$new(test_skill_path) scripts <- skill$list_scripts() expect_type(scripts, "character") expect_true("hello.R" %in% scripts) }) test_that("Skill$list_scripts returns empty for skill without scripts dir", { tmp_dir <- tempfile() dir.create(tmp_dir) on.exit(unlink(tmp_dir, recursive = TRUE)) writeLines(c("---", "name: test", "---"), file.path(tmp_dir, "SKILL.md")) skill <- Skill$new(tmp_dir) scripts <- skill$list_scripts() expect_equal(length(scripts), 0) }) # === Tests for Skill$execute_script === test_that("Skill$execute_script runs script and returns result", { skip_if_not(dir.exists(test_skill_path), "Test fixtures not found") skill <- Skill$new(test_skill_path) result <- skill$execute_script("hello.R", list(name = "Claude")) expect_equal(result, "Hello, Claude from test_skill!") }) test_that("Skill$execute_script uses default when no args provided", { skip_if_not(dir.exists(test_skill_path), "Test fixtures not found") skill <- Skill$new(test_skill_path) result <- skill$execute_script("hello.R") expect_equal(result, "Hello, World from test_skill!") }) test_that("Skill$execute_script returns error message for missing script", { skip_if_not(dir.exists(test_skill_path), "Test fixtures not found") skill <- Skill$new(test_skill_path) expect_error(skill$execute_script("nonexistent.R"), "Script not found") }) # === Tests for Skill$print === test_that("Skill$print outputs summary", { skip_if_not(dir.exists(test_skill_path), "Test fixtures not found") skill <- Skill$new(test_skill_path) output <- capture.output(print(skill)) expect_true(any(grepl("", output))) expect_true(any(grepl("test_skill", output))) })