internal/app/skill_requirement_test.go
Rama main · réplica de lectura del repositorio original.
package app
import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/yeomyeonggeori/blueclaw/internal/agentruntime"
"github.com/yeomyeonggeori/blueclaw/internal/capability"
"github.com/yeomyeonggeori/blueclaw/internal/config"
)
const skillRequiringAToken = `---
name: internkim-api
description: Drives the company workspace over its public API.
compatibility: Requires python3, network access, and a personal access token in INTERNKIM_TOKEN.
metadata:
kim.intern.requires-environment: "INTERNKIM_TOKEN"
---
Body.
`
func instructionsForSkillRequiringAToken(t *testing.T) agentInstructions {
t.Helper()
deliveredSkillsPath := t.TempDir()
skillDirectoryPath := filepath.Join(deliveredSkillsPath, "internkim-api")
if errorValue := os.MkdirAll(skillDirectoryPath, 0o755); errorValue != nil {
t.Fatal(errorValue)
}
if errorValue := os.WriteFile(filepath.Join(skillDirectoryPath, "SKILL.md"), []byte(skillRequiringAToken), 0o600); errorValue != nil {
t.Fatal(errorValue)
}
t.Setenv("BLUECLAW_BUNDLED_SKILLS_PATH", deliveredSkillsPath)
runtimeConfiguration := config.RuntimeConfiguration{}
runtimeConfiguration.Terminal.WorkspaceRootPath = t.TempDir()
return loadAgentInstructions(runtimeConfiguration, agentruntime.NewCapabilityRegistry(capability.Client{}, nil))
}
func skillNames(instructions agentInstructions) []string {
names := []string{}
for _, skillInstruction := range instructions.Bundle.Skills {
names = append(names, skillInstruction.Name)
}
return names
}
func TestASkillWhoseVariableThisHostLacksStaysOutOfThePrompt(t *testing.T) {
t.Setenv("INTERNKIM_TOKEN", "")
instructions := instructionsForSkillRequiringAToken(t)
for _, skillName := range skillNames(instructions) {
if skillName == "internkim-api" {
t.Fatal("a skill this host cannot run was offered to the agent anyway")
}
}
if len(instructions.UnavailableSkills) != 1 {
t.Fatalf("expected the skill listed as unavailable, got %+v", instructions.UnavailableSkills)
}
unavailableSkill := instructions.UnavailableSkills[0]
if unavailableSkill.Name != "internkim-api" {
t.Fatalf("expected internkim-api, got %q", unavailableSkill.Name)
}
if len(unavailableSkill.MissingEnvironmentVariables) != 1 || unavailableSkill.MissingEnvironmentVariables[0] != "INTERNKIM_TOKEN" {
t.Fatalf("the reason must name the variable, got %v", unavailableSkill.MissingEnvironmentVariables)
}
if unavailableSkill.Path == "" {
t.Fatal("the inventory must say where the skill was read from")
}
}
func TestTheSameSkillIsSelectableOnceItsVariableIsSet(t *testing.T) {
t.Setenv("INTERNKIM_TOKEN", "ik_a-token")
instructions := instructionsForSkillRequiringAToken(t)
selected := false
for _, skillName := range skillNames(instructions) {
if skillName == "internkim-api" {
selected = true
}
}
if !selected {
t.Fatal("a host that meets the requirement must get the skill")
}
if len(instructions.UnavailableSkills) != 0 {
t.Fatalf("nothing is unavailable here, got %+v", instructions.UnavailableSkills)
}
}
const skillRequiringACapabilityTool = `---
name: messages
description: Writes to a person on the messenger.
metadata:
kim.intern.tool-references: "message_send"
---
Body.
`
func TestASkillNamingACapabilityToolIsSelectableFromTheLiveRegistry(t *testing.T) {
deliveredSkillsPath := t.TempDir()
skillDirectoryPath := filepath.Join(deliveredSkillsPath, "messages")
if errorValue := os.MkdirAll(skillDirectoryPath, 0o755); errorValue != nil {
t.Fatal(errorValue)
}
if errorValue := os.WriteFile(filepath.Join(skillDirectoryPath, "SKILL.md"), []byte(skillRequiringACapabilityTool), 0o600); errorValue != nil {
t.Fatal(errorValue)
}
t.Setenv("BLUECLAW_BUNDLED_SKILLS_PATH", deliveredSkillsPath)
server := httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, _ *http.Request) {
responseWriter.Header().Set("Content-Type", "application/json")
_, _ = responseWriter.Write([]byte(`{"deviceCapabilities":[{"name":"message_send"}]}`))
}))
defer server.Close()
runtimeConfiguration := config.RuntimeConfiguration{}
runtimeConfiguration.Terminal.WorkspaceRootPath = t.TempDir()
capabilityRegistry := agentruntime.NewCapabilityRegistry(
capability.Client{Endpoint: server.URL, HTTPClient: server.Client()},
nil,
)
instructions := loadAgentInstructions(runtimeConfiguration, capabilityRegistry)
if len(instructions.UnavailableSkills) != 0 {
t.Fatalf("expected the live registry to offer message_send, got %+v", instructions.UnavailableSkills)
}
selected := false
for _, skillName := range skillNames(instructions) {
if skillName == "messages" {
selected = true
}
}
if !selected {
t.Fatalf("expected the skill in the prompt, got %v", skillNames(instructions))
}
}