113 lines
3.0 KiB
Go
113 lines
3.0 KiB
Go
package skill_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"reasonix/internal/skill"
|
|
)
|
|
|
|
func TestReasonixGuideBuiltinRegistered(t *testing.T) {
|
|
store := skill.New(skill.Options{HomeDir: t.TempDir(), DisableBuiltins: false})
|
|
sk, ok := store.Read("reasonix-guide")
|
|
if !ok {
|
|
t.Fatal("reasonix-guide must be registered as a builtin")
|
|
}
|
|
if sk.Scope != skill.ScopeBuiltin {
|
|
t.Fatalf("scope = %s", sk.Scope)
|
|
}
|
|
if sk.RunAs != skill.RunInline {
|
|
t.Fatalf("runAs = %s", sk.RunAs)
|
|
}
|
|
if sk.Description == "" {
|
|
t.Fatal("description required for index line")
|
|
}
|
|
if !strings.Contains(sk.Body, "doctor capabilities") {
|
|
t.Fatal("body missing doctor capabilities guidance")
|
|
}
|
|
}
|
|
|
|
func TestReasonixGuideIndexLineOnly(t *testing.T) {
|
|
store := skill.New(skill.Options{HomeDir: t.TempDir()})
|
|
list := store.List()
|
|
var guide skill.Skill
|
|
found := false
|
|
for _, s := range list {
|
|
if s.Name == "reasonix-guide" {
|
|
guide = s
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("reasonix-guide missing from List")
|
|
}
|
|
idx := skill.IndexBlock(list)
|
|
if !strings.Contains(idx, "reasonix-guide") {
|
|
t.Fatal("index missing reasonix-guide line")
|
|
}
|
|
// Body must not appear in the index block.
|
|
if strings.Contains(idx, "First action") || strings.Contains(idx, skBodySnippet(guide)) {
|
|
t.Fatal("skill body leaked into system-prompt index")
|
|
}
|
|
// Exactly one index line for the skill name.
|
|
if c := strings.Count(idx, "- reasonix-guide"); c != 1 {
|
|
t.Fatalf("index lines for reasonix-guide = %d, want 1", c)
|
|
}
|
|
}
|
|
|
|
func skBodySnippet(sk skill.Skill) string {
|
|
body := strings.TrimSpace(sk.Body)
|
|
if len(body) > 40 {
|
|
return body[:40]
|
|
}
|
|
return body
|
|
}
|
|
|
|
func TestReasonixGuideOverriddenByProject(t *testing.T) {
|
|
home := t.TempDir()
|
|
root := t.TempDir()
|
|
store := skill.New(skill.Options{HomeDir: home, ProjectRoot: root})
|
|
// Create project override.
|
|
path, err := store.CreateWithContent("reasonix-guide", skill.ScopeProject, "---\ndescription: override\nrunAs: inline\n---\nproject body\n")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = path
|
|
store2 := skill.New(skill.Options{HomeDir: home, ProjectRoot: root})
|
|
sk, ok := store2.Read("reasonix-guide")
|
|
if !ok {
|
|
t.Fatal("expected override")
|
|
}
|
|
if sk.Scope != skill.ScopeProject {
|
|
t.Fatalf("scope = %s, want project", sk.Scope)
|
|
}
|
|
if !strings.Contains(sk.Body, "project body") {
|
|
t.Fatalf("body = %q", sk.Body)
|
|
}
|
|
}
|
|
|
|
func TestReasonixGuideDisabled(t *testing.T) {
|
|
store := skill.New(skill.Options{
|
|
HomeDir: t.TempDir(),
|
|
DisabledNames: []string{"reasonix-guide"},
|
|
})
|
|
if _, ok := store.Read("reasonix-guide"); ok {
|
|
t.Fatal("disabled builtin should not be readable")
|
|
}
|
|
for _, s := range store.List() {
|
|
if s.Name == "reasonix-guide" {
|
|
t.Fatal("disabled builtin should not be listed")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReasonixGuideIndexStableAcrossCalls(t *testing.T) {
|
|
store := skill.New(skill.Options{HomeDir: t.TempDir()})
|
|
a := skill.IndexBlock(store.List())
|
|
b := skill.IndexBlock(store.List())
|
|
if a != b {
|
|
t.Fatal("skills index not byte-stable across List calls")
|
|
}
|
|
}
|