mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-04-14 23:12:37 +00:00
introduce the agent skills standard for packaging reusable instructions that agents can discover and activate at runtime. - skills defined via SKILL.md with yaml frontmatter and markdown body - three-level progressive disclosure: metadata, instructions, resources - filesystem discovery with directory name validation - skill lifecycle events (discovery, loaded, activated, failed) - crew-level skills resolved once and shared across agents - skill context injected into both task execution and standalone kickoff
94 lines
2.7 KiB
Python
94 lines
2.7 KiB
Python
"""Tests for skills validation."""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from crewai.skills.models import SkillFrontmatter
|
|
from crewai.skills.validation import (
|
|
MAX_SKILL_NAME_LENGTH,
|
|
validate_directory_name,
|
|
)
|
|
|
|
|
|
def _make(name: str) -> SkillFrontmatter:
|
|
"""Create a SkillFrontmatter with the given name."""
|
|
return SkillFrontmatter(name=name, description="desc")
|
|
|
|
|
|
class TestSkillNameValidation:
|
|
"""Tests for skill name constraints via SkillFrontmatter."""
|
|
|
|
def test_simple_name(self) -> None:
|
|
assert _make("web-search").name == "web-search"
|
|
|
|
def test_single_word(self) -> None:
|
|
assert _make("search").name == "search"
|
|
|
|
def test_numeric(self) -> None:
|
|
assert _make("tool3").name == "tool3"
|
|
|
|
def test_all_digits(self) -> None:
|
|
assert _make("123").name == "123"
|
|
|
|
def test_single_char(self) -> None:
|
|
assert _make("a").name == "a"
|
|
|
|
def test_max_length(self) -> None:
|
|
name = "a" * MAX_SKILL_NAME_LENGTH
|
|
assert _make(name).name == name
|
|
|
|
def test_multi_hyphen_segments(self) -> None:
|
|
assert _make("my-cool-skill").name == "my-cool-skill"
|
|
|
|
def test_empty_raises(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
_make("")
|
|
|
|
def test_too_long_raises(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
_make("a" * (MAX_SKILL_NAME_LENGTH + 1))
|
|
|
|
def test_uppercase_raises(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
_make("MySkill")
|
|
|
|
def test_leading_hyphen_raises(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
_make("-skill")
|
|
|
|
def test_trailing_hyphen_raises(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
_make("skill-")
|
|
|
|
def test_consecutive_hyphens_raises(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
_make("my--skill")
|
|
|
|
def test_underscore_raises(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
_make("my_skill")
|
|
|
|
def test_space_raises(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
_make("my skill")
|
|
|
|
def test_special_chars_raises(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
_make("skill@v1")
|
|
|
|
|
|
class TestValidateDirectoryName:
|
|
"""Tests for validate_directory_name."""
|
|
|
|
def test_matching_names(self, tmp_path: Path) -> None:
|
|
skill_dir = tmp_path / "my-skill"
|
|
skill_dir.mkdir()
|
|
validate_directory_name(skill_dir, "my-skill")
|
|
|
|
def test_mismatched_names(self, tmp_path: Path) -> None:
|
|
skill_dir = tmp_path / "other-name"
|
|
skill_dir.mkdir()
|
|
with pytest.raises(ValueError, match="does not match"):
|
|
validate_directory_name(skill_dir, "my-skill")
|