All checks were successful
CI / build (push) Successful in 2m37s
- Add AES-256-GCM encryption for API keys (internal/secret) - Add dangerous command detection in terminal - Add muyue doctor command for system health checks - Add scanner TTL cache, orchestrator history mutex, shared HTTP client - Deduplicate MCP config generation, refactor skills YAML parser - Add XDG-compliant config dir with legacy migration - Add cleanup on all TUI quit paths - Add 8 test files (config, workflow, skills, orchestrator, version, platform, scanner, secret) - Update CI to actions/setup-go@v5 - Add CHANGELOG.md, update README and Makefile 🤖 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
201 lines
4.2 KiB
Go
201 lines
4.2 KiB
Go
package skills
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseSkillWithYAML(t *testing.T) {
|
|
data := []byte(`---
|
|
name: test-skill
|
|
description: A test skill
|
|
author: test
|
|
version: "1.0"
|
|
target: both
|
|
tags:
|
|
- test
|
|
- demo
|
|
---
|
|
# Test Skill Content
|
|
This is the body.
|
|
`)
|
|
|
|
skill, err := parseSkill(data)
|
|
if err != nil {
|
|
t.Fatalf("parseSkill failed: %v", err)
|
|
}
|
|
if skill.Description != "A test skill" {
|
|
t.Errorf("Expected 'A test skill', got %s", skill.Description)
|
|
}
|
|
if skill.Author != "test" {
|
|
t.Errorf("Expected 'test', got %s", skill.Author)
|
|
}
|
|
if skill.Version != "1.0" {
|
|
t.Errorf("Expected '1.0', got %s", skill.Version)
|
|
}
|
|
if skill.Target != "both" {
|
|
t.Errorf("Expected 'both', got %s", skill.Target)
|
|
}
|
|
if len(skill.Tags) != 2 {
|
|
t.Errorf("Expected 2 tags, got %d", len(skill.Tags))
|
|
}
|
|
if skill.Content == "" {
|
|
t.Error("Content should not be empty")
|
|
}
|
|
}
|
|
|
|
func TestParseSkillNoFrontmatter(t *testing.T) {
|
|
data := []byte("Just plain content here")
|
|
skill, err := parseSkill(data)
|
|
if err != nil {
|
|
t.Fatalf("parseSkill failed: %v", err)
|
|
}
|
|
if skill.Content != "Just plain content here" {
|
|
t.Errorf("Unexpected content: %s", skill.Content)
|
|
}
|
|
}
|
|
|
|
func TestParseSkillIncompleteFrontmatter(t *testing.T) {
|
|
data := []byte("---\nname: incomplete\n---\nBody content")
|
|
skill, err := parseSkill(data)
|
|
if err != nil {
|
|
t.Fatalf("parseSkill failed: %v", err)
|
|
}
|
|
if skill.Content != "Body content" {
|
|
t.Errorf("Expected 'Body content', got %s", skill.Content)
|
|
}
|
|
}
|
|
|
|
func TestRenderSkill(t *testing.T) {
|
|
skill := &Skill{
|
|
Name: "test",
|
|
Description: "A test",
|
|
Author: "author",
|
|
Version: "1.0",
|
|
Target: "both",
|
|
Tags: []string{"a", "b"},
|
|
Content: "Body",
|
|
}
|
|
|
|
rendered := renderSkill(skill)
|
|
if rendered == "" {
|
|
t.Error("Rendered skill should not be empty")
|
|
}
|
|
if len(rendered) < 20 {
|
|
t.Error("Rendered skill seems too short")
|
|
}
|
|
}
|
|
|
|
func TestListEmpty(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
origHome := os.Getenv("HOME")
|
|
os.Setenv("HOME", tmpDir)
|
|
defer os.Setenv("HOME", origHome)
|
|
|
|
skills, err := List()
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
if len(skills) != 0 {
|
|
t.Errorf("Expected 0 skills, got %d", len(skills))
|
|
}
|
|
}
|
|
|
|
func TestCreateAndGet(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
origHome := os.Getenv("HOME")
|
|
os.Setenv("HOME", tmpDir)
|
|
defer os.Setenv("HOME", origHome)
|
|
|
|
skill := &Skill{
|
|
Name: "test-skill",
|
|
Description: "Test description",
|
|
Content: "Test content body",
|
|
Author: "tester",
|
|
Version: "0.1",
|
|
Target: "both",
|
|
}
|
|
|
|
if err := Create(skill); err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
dir, _ := SkillsDir()
|
|
skillPath := filepath.Join(dir, "test-skill", "SKILL.md")
|
|
if _, err := os.Stat(skillPath); os.IsNotExist(err) {
|
|
t.Error("Skill file should exist")
|
|
}
|
|
|
|
got, err := Get("test-skill")
|
|
if err != nil {
|
|
t.Fatalf("Get failed: %v", err)
|
|
}
|
|
if got.Name != "test-skill" {
|
|
t.Errorf("Expected test-skill, got %s", got.Name)
|
|
}
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
origHome := os.Getenv("HOME")
|
|
os.Setenv("HOME", tmpDir)
|
|
defer os.Setenv("HOME", origHome)
|
|
|
|
skill := &Skill{
|
|
Name: "to-delete",
|
|
Description: "Will be deleted",
|
|
Content: "content",
|
|
Target: "both",
|
|
}
|
|
Create(skill)
|
|
|
|
if err := Delete("to-delete"); err != nil {
|
|
t.Fatalf("Delete failed: %v", err)
|
|
}
|
|
|
|
_, err := Get("to-delete")
|
|
if err == nil {
|
|
t.Error("Skill should be deleted")
|
|
}
|
|
}
|
|
|
|
func TestBuildAIGeneratePrompt(t *testing.T) {
|
|
prompt := BuildAIGeneratePrompt("docker", "Set up Docker", "both")
|
|
if prompt == "" {
|
|
t.Error("Prompt should not be empty")
|
|
}
|
|
if len(prompt) < 50 {
|
|
t.Error("Prompt seems too short")
|
|
}
|
|
}
|
|
|
|
func TestInstallBuiltinSkills(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
origHome := os.Getenv("HOME")
|
|
os.Setenv("HOME", tmpDir)
|
|
defer os.Setenv("HOME", origHome)
|
|
|
|
if err := InstallBuiltinSkills(); err != nil {
|
|
t.Fatalf("InstallBuiltinSkills failed: %v", err)
|
|
}
|
|
|
|
skills, err := List()
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
if len(skills) == 0 {
|
|
t.Error("Expected at least one builtin skill")
|
|
}
|
|
|
|
found := false
|
|
for _, s := range skills {
|
|
if s.Name == "env-setup" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Expected env-setup skill")
|
|
}
|
|
}
|