feat: security hardening, tests, doctor command, CI update, CHANGELOG
All checks were successful
CI / build (push) Successful in 2m37s
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>
This commit is contained in:
200
internal/skills/skills_test.go
Normal file
200
internal/skills/skills_test.go
Normal file
@@ -0,0 +1,200 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user