All checks were successful
Stable Release / stable (push) Successful in 1m34s
Major additions: - RAG pipeline (indexing, chunking, search) with sidebar upload button - Memory system with CRUD API - Plugins and lessons modules - MCP discovery and MCP server - Advanced skills (auto-create, conditional, improver) - Agent browser/image support, delegate, sessions - File editor with CodeMirror in split panes - Markdown rendering via react-markdown + KaTeX + highlight.js - Raw markdown toggle - PWA manifest + service worker - Extension UI redesign with new design tokens and studio-style chat - Pipeline API for chat streaming - Mobile responsive layout 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
178 lines
4.2 KiB
Go
178 lines
4.2 KiB
Go
package skills
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCheckActivationNoConditions(t *testing.T) {
|
|
skill := &Skill{
|
|
Name: "test-skill",
|
|
Description: "A test skill",
|
|
}
|
|
|
|
result := CheckActivation(skill, []string{"terminal"})
|
|
if !result.Active {
|
|
t.Error("expected skill with no conditions to be active")
|
|
}
|
|
}
|
|
|
|
func TestCheckActivationRequiresTools(t *testing.T) {
|
|
skill := &Skill{
|
|
Name: "docker-setup",
|
|
RequiresTools: []string{"terminal", "docker"},
|
|
}
|
|
|
|
result := CheckActivation(skill, []string{"terminal", "docker"})
|
|
if !result.Active {
|
|
t.Error("expected skill to be active when all required tools present")
|
|
}
|
|
|
|
result = CheckActivation(skill, []string{"terminal"})
|
|
if result.Active {
|
|
t.Error("expected skill to be inactive when required tool missing")
|
|
}
|
|
}
|
|
|
|
func TestCheckActivationFallbackForTools(t *testing.T) {
|
|
skill := &Skill{
|
|
Name: "basic-review",
|
|
FallbackForTools: []string{"crush_run", "claude_run"},
|
|
}
|
|
|
|
result := CheckActivation(skill, []string{"terminal"})
|
|
if !result.Active {
|
|
t.Error("expected fallback skill to activate when primary tools absent")
|
|
}
|
|
|
|
result = CheckActivation(skill, []string{"crush_run", "claude_run"})
|
|
if result.Active {
|
|
t.Error("expected fallback skill to stay inactive when primary tools present")
|
|
}
|
|
}
|
|
|
|
func TestFilterActiveSkills(t *testing.T) {
|
|
skills := []Skill{
|
|
{Name: "basic", Description: "basic"},
|
|
{Name: "needs-docker", RequiresTools: []string{"docker"}},
|
|
{Name: "fallback-review", FallbackForTools: []string{"crush_run"}},
|
|
}
|
|
|
|
active := FilterActiveSkills(skills, []string{"terminal"})
|
|
if len(active) != 2 {
|
|
t.Errorf("expected 2 active skills, got %d", len(active))
|
|
}
|
|
}
|
|
|
|
func TestGroupByReadiness(t *testing.T) {
|
|
skills := []Skill{
|
|
{Name: "basic", Description: "basic"},
|
|
{Name: "needs-docker", RequiresTools: []string{"docker"}},
|
|
}
|
|
|
|
available, needsSetup, unsupported := GroupByReadiness(skills, []string{})
|
|
if len(available) != 1 {
|
|
t.Errorf("expected 1 available, got %d", len(available))
|
|
}
|
|
if len(unsupported) != 1 {
|
|
t.Errorf("expected 1 unsupported, got %d", len(unsupported))
|
|
}
|
|
_ = needsSetup
|
|
}
|
|
|
|
func TestAnalyzeConversation(t *testing.T) {
|
|
snippets := []ConversationSnippet{
|
|
{Role: "assistant", Content: "go test ./... -race", Timestamp: time.Now()},
|
|
{Role: "assistant", Content: "go test ./... -race -cover", Timestamp: time.Now()},
|
|
{Role: "assistant", Content: "go test ./internal/... -v", Timestamp: time.Now()},
|
|
}
|
|
|
|
proposals := AnalyzeConversation(snippets)
|
|
if len(proposals) == 0 {
|
|
t.Error("expected at least one proposal from recurring patterns")
|
|
}
|
|
|
|
for _, p := range proposals {
|
|
if p.Confidence <= 0 {
|
|
t.Error("expected positive confidence")
|
|
}
|
|
if p.CreatedFrom != "conversation" {
|
|
t.Errorf("expected created_from=conversation, got %s", p.CreatedFrom)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCategorize(t *testing.T) {
|
|
tests := []struct {
|
|
pattern string
|
|
want string
|
|
}{
|
|
{"go test", "testing"},
|
|
{"docker build", "devops"},
|
|
{"git commit", "workflow"},
|
|
{"npm test", "testing"},
|
|
{"make", "build"},
|
|
{"unknown", "general"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := categorize(tt.pattern)
|
|
if got != tt.want {
|
|
t.Errorf("categorize(%q) = %q, want %q", tt.pattern, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestImproverAnalyze(t *testing.T) {
|
|
improver, err := NewSkillImprover()
|
|
if err != nil {
|
|
t.Fatalf("new improver: %v", err)
|
|
}
|
|
|
|
skill := &Skill{
|
|
Name: "test-skill",
|
|
Description: "A test skill",
|
|
Content: "# Test\n\nSome basic content without structure.",
|
|
}
|
|
|
|
suggestions, err := improver.Analyze(skill, "")
|
|
if err != nil {
|
|
t.Fatalf("analyze: %v", err)
|
|
}
|
|
|
|
if len(suggestions) == 0 {
|
|
t.Error("expected improvement suggestions for minimal skill")
|
|
}
|
|
}
|
|
|
|
func TestImproverAnalyzeComplete(t *testing.T) {
|
|
improver, _ := NewSkillImprover()
|
|
|
|
skill := &Skill{
|
|
Name: "complete-skill",
|
|
Description: "A well-structured skill",
|
|
Content: `# Complete Skill
|
|
|
|
## Steps
|
|
|
|
1. Do step one
|
|
2. Do step two
|
|
|
|
## Error Handling
|
|
|
|
- Handle error A
|
|
- Handle error B
|
|
|
|
## When to use
|
|
|
|
Use this skill when doing X.
|
|
`,
|
|
Tags: []string{"testing", "go"},
|
|
}
|
|
|
|
suggestions, _ := improver.Analyze(skill, "testing go code")
|
|
if len(suggestions) > 2 {
|
|
t.Errorf("expected few suggestions for complete skill, got %d", len(suggestions))
|
|
}
|
|
}
|