bdf635e547
Double détection dans scan_skills() : - Bundle : dossier avec SKILL.md, agents: lu dans le frontmatter, copie récursive du dossier complet (scripts/, templates/, references/) - Legacy : <agent>.md existant, agent déduit du nom de fichier - Priorité bundle sur legacy pour un même cat/skill/agent - Nouveau champ get_frontmatter_agents() pour parser agents: [...] - SKILLS_LIST étendu : ...|kind|source_path (10 champs) - install_selected() branche sur kind=bundle vs legacy - preview_script utilise source_path pour trouver le fichier à afficher ha-log-investigator (bundle avec scripts/ et references/) est maintenant détecté et installé correctement. Tests : section 5 ajoutée — 9 nouveaux cas (bundle, legacy, références ignorées, doublon, priorité, source_path, accessibilité fichiers). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
276 lines
9.0 KiB
Bash
Executable File
276 lines
9.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Tests unitaires pour install.sh
|
|
# Usage : cd tests && bash test_install.sh
|
|
set -euo pipefail
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
|
|
assert_eq() {
|
|
local desc="$1" expected="$2" actual="$3"
|
|
if [[ "$expected" == "$actual" ]]; then
|
|
echo " ✓ $desc"
|
|
(( PASS++ )) || true
|
|
else
|
|
echo " ✗ $desc"
|
|
echo " attendu : '$expected'"
|
|
echo " obtenu : '$actual'"
|
|
(( FAIL++ )) || true
|
|
fi
|
|
}
|
|
|
|
assert_true() {
|
|
local desc="$1"
|
|
shift
|
|
if eval "$@" 2>/dev/null; then
|
|
echo " ✓ $desc"
|
|
(( PASS++ )) || true
|
|
else
|
|
echo " ✗ $desc"
|
|
(( FAIL++ )) || true
|
|
fi
|
|
}
|
|
|
|
assert_false() {
|
|
local desc="$1"
|
|
shift
|
|
if ! eval "$@" 2>/dev/null; then
|
|
echo " ✓ $desc"
|
|
(( PASS++ )) || true
|
|
else
|
|
echo " ✗ $desc"
|
|
(( FAIL++ )) || true
|
|
fi
|
|
}
|
|
|
|
# Charge install.sh sans exécuter main()
|
|
load_install() {
|
|
local tmp; tmp=$(mktemp)
|
|
# Supprime l'appel final à main et desactive set -e pour le sourcing
|
|
grep -v '^main "\$@"' ../install.sh > "$tmp"
|
|
set +e
|
|
# shellcheck disable=SC1090
|
|
source "$tmp"
|
|
set -e
|
|
rm -f "$tmp"
|
|
}
|
|
|
|
echo ""
|
|
echo "══════════════════════════════════"
|
|
echo " Tests install.sh"
|
|
echo "══════════════════════════════════"
|
|
echo ""
|
|
|
|
load_install
|
|
# Désactive le trap cleanup() hérité de install.sh (évite l'erreur nounset sur STATE_FILE)
|
|
trap - EXIT
|
|
|
|
# ── 1. Comparaison de versions ────────────────────────────────────
|
|
echo "1. version_is_newer()"
|
|
assert_true "1.2.0 > 1.0.0" "version_is_newer '1.0.0' '1.2.0'"
|
|
assert_false "1.0.0 n'est pas > 1.2.0" "version_is_newer '1.2.0' '1.0.0'"
|
|
assert_false "1.0.0 == 1.0.0 → false" "version_is_newer '1.0.0' '1.0.0'"
|
|
assert_true "2.0.0 > 1.9.9" "version_is_newer '1.9.9' '2.0.0'"
|
|
assert_true "1.0.1 > 1.0.0" "version_is_newer '1.0.0' '1.0.1'"
|
|
|
|
# ── 2. Parsing frontmatter ────────────────────────────────────────
|
|
echo ""
|
|
echo "2. get_frontmatter_field()"
|
|
|
|
TMP_SKILL=$(mktemp --suffix=.md)
|
|
cat > "$TMP_SKILL" << 'SKILL_EOF'
|
|
---
|
|
name: test-skill
|
|
version: 2.1.0
|
|
description: Skill de test pour vérification
|
|
agents: [claude-code]
|
|
category: dev
|
|
tags: [bash, test]
|
|
---
|
|
# Contenu du skill
|
|
SKILL_EOF
|
|
|
|
assert_eq "version" "2.1.0" "$(get_frontmatter_field "$TMP_SKILL" "version")"
|
|
assert_eq "name" "test-skill" "$(get_frontmatter_field "$TMP_SKILL" "name")"
|
|
assert_eq "category" "dev" "$(get_frontmatter_field "$TMP_SKILL" "category")"
|
|
assert_eq "champ absent → vide" "" "$(get_frontmatter_field "$TMP_SKILL" "nonexistent")"
|
|
rm -f "$TMP_SKILL"
|
|
|
|
# ── 3. Chemins de destination ─────────────────────────────────────
|
|
echo ""
|
|
echo "3. get_dest_path()"
|
|
|
|
assert_eq "claude global" \
|
|
"$HOME/.claude/skills/dev/debugging/SKILL.md" \
|
|
"$(get_dest_path "dev" "debugging" "claude-code" "global")"
|
|
|
|
assert_eq "claude local" \
|
|
".claude/skills/dev/debugging/SKILL.md" \
|
|
"$(get_dest_path "dev" "debugging" "claude-code" "local")"
|
|
|
|
assert_eq "gemini local" \
|
|
".gemini/skills/dev/debugging/SKILL.md" \
|
|
"$(get_dest_path "dev" "debugging" "gemini-cli" "local")"
|
|
|
|
assert_eq "codex global" \
|
|
"$HOME/.codex/skills/jardinage/arrosage/SKILL.md" \
|
|
"$(get_dest_path "jardinage" "arrosage" "codex" "global")"
|
|
|
|
assert_eq "hermes local" \
|
|
".hermes/skills/electronique/arduino/SKILL.md" \
|
|
"$(get_dest_path "electronique" "arduino" "hermes" "local")"
|
|
|
|
# ── 4. Gestion d'état ─────────────────────────────────────────────
|
|
echo ""
|
|
echo "4. state_get() / state_cycle() / make_key()"
|
|
|
|
STATE_FILE=$(mktemp)
|
|
export STATE_FILE
|
|
|
|
# Teste make_key — prend une entrée "cat|skill|agent|etat|repo_ver|local_ver"
|
|
assert_eq "make_key basique" \
|
|
"dev_debugging_claude_code" \
|
|
"$(make_key "dev|debugging|claude-code|new|1.0.0|")"
|
|
|
|
assert_eq "make_key avec tirets" \
|
|
"dev_git_workflow_claude_code" \
|
|
"$(make_key "dev|git-workflow|claude-code|new|1.0.0|")"
|
|
|
|
# Initialise un état
|
|
echo "dev_debugging_claude_code=local" > "$STATE_FILE"
|
|
|
|
assert_eq "state_get local" "local" "$(state_get "dev_debugging_claude_code")"
|
|
|
|
state_cycle "dev_debugging_claude_code" "ok"
|
|
assert_eq "cycle local→global" "global" "$(state_get "dev_debugging_claude_code")"
|
|
|
|
state_cycle "dev_debugging_claude_code" "ok"
|
|
assert_eq "cycle global→skip" "skip" "$(state_get "dev_debugging_claude_code")"
|
|
|
|
state_cycle "dev_debugging_claude_code" "ok"
|
|
assert_eq "cycle skip→local (état ok)" "local" "$(state_get "dev_debugging_claude_code")"
|
|
|
|
# Teste le cycle avec état upd
|
|
echo "dev_debugging_claude_code=skip" > "$STATE_FILE"
|
|
state_cycle "dev_debugging_claude_code" "upd"
|
|
assert_eq "cycle skip→update (état upd)" "update" "$(state_get "dev_debugging_claude_code")"
|
|
|
|
state_cycle "dev_debugging_claude_code" "upd"
|
|
assert_eq "cycle update→local" "local" "$(state_get "dev_debugging_claude_code")"
|
|
|
|
rm -f "$STATE_FILE"
|
|
unset STATE_FILE || true
|
|
|
|
# ── 5. Détection bundle / legacy ─────────────────────────────────
|
|
echo ""
|
|
echo "5. get_frontmatter_agents() / scan_skills() double format"
|
|
|
|
TMP_DIR=$(mktemp -d)
|
|
|
|
# Skill bundle : SKILL.md + agents: [claude-code, codex]
|
|
mkdir -p "$TMP_DIR/skills/infra/mon-bundle"
|
|
cat > "$TMP_DIR/skills/infra/mon-bundle/SKILL.md" << 'EOF'
|
|
---
|
|
name: mon-bundle
|
|
version: 1.2.0
|
|
agents: [claude-code, codex]
|
|
description: Skill bundle test
|
|
tags: [test]
|
|
---
|
|
# Mon bundle
|
|
EOF
|
|
mkdir -p "$TMP_DIR/skills/infra/mon-bundle/scripts"
|
|
touch "$TMP_DIR/skills/infra/mon-bundle/scripts/helper.sh"
|
|
|
|
# Skill legacy : claude-code.md
|
|
mkdir -p "$TMP_DIR/skills/dev/ancien"
|
|
cat > "$TMP_DIR/skills/dev/ancien/claude-code.md" << 'EOF'
|
|
---
|
|
name: ancien
|
|
version: 0.5.0
|
|
description: Skill legacy
|
|
tags: [legacy]
|
|
---
|
|
# Ancien skill
|
|
EOF
|
|
|
|
# Fichier Markdown de support (ne doit PAS être détecté comme skill)
|
|
mkdir -p "$TMP_DIR/skills/infra/mon-bundle/references"
|
|
echo "# Ref doc" > "$TMP_DIR/skills/infra/mon-bundle/references/notes.md"
|
|
|
|
assert_eq "agents: [claude-code, codex] → 2 agents" \
|
|
"claude-code
|
|
codex" \
|
|
"$(get_frontmatter_agents "$TMP_DIR/skills/infra/mon-bundle/SKILL.md")"
|
|
|
|
assert_eq "agents: absent → vide" \
|
|
"" \
|
|
"$(get_frontmatter_agents "$TMP_DIR/skills/dev/ancien/claude-code.md")"
|
|
|
|
# scan_skills avec les deux formats
|
|
REPO_DIR="$TMP_DIR"
|
|
DETECTED_AGENTS=()
|
|
SKILLS_TAG=""
|
|
STATE_FILE=$(mktemp)
|
|
scan_skills 2>/dev/null
|
|
|
|
# Compter les entrées trouvées
|
|
bundle_count=0; legacy_count=0; ref_count=0
|
|
for e in "${SKILLS_LIST[@]}"; do
|
|
IFS='|' read -r _ _ _ _ _ _ _ _ kind source_path <<< "$e"
|
|
[[ "$kind" == "bundle" ]] && (( bundle_count++ )) || true
|
|
[[ "$kind" == "legacy" ]] && (( legacy_count++ )) || true
|
|
# Vérifier qu'aucune entrée ne vient de references/notes.md
|
|
[[ "$source_path" == *"notes.md"* ]] && (( ref_count++ )) || true
|
|
done
|
|
|
|
assert_eq "bundle détecté 2 fois (2 agents)" "2" "$bundle_count"
|
|
assert_eq "legacy détecté 1 fois" "1" "$legacy_count"
|
|
assert_eq "fichier references/ ignoré" "0" "$ref_count"
|
|
|
|
# Vérifier que kind=bundle et source_path pointent sur le dossier
|
|
first_bundle=""
|
|
for e in "${SKILLS_LIST[@]}"; do
|
|
IFS='|' read -r _ _ _ _ _ _ _ _ kind source_path <<< "$e"
|
|
[[ "$kind" == "bundle" ]] && { first_bundle="$source_path"; break; }
|
|
done
|
|
assert_eq "source_path bundle = dossier du skill" \
|
|
"$TMP_DIR/skills/infra/mon-bundle" \
|
|
"$first_bundle"
|
|
|
|
# Vérifier que SKILL.md du bundle est lisible pour le preview
|
|
assert_true "SKILL.md bundle accessible" "[[ -f '${first_bundle}/SKILL.md' ]]"
|
|
assert_true "scripts/ du bundle accessible" "[[ -f '${first_bundle}/scripts/helper.sh' ]]"
|
|
|
|
# Priorité bundle sur legacy : si SKILL.md existe, le <agent>.md ne duplique pas
|
|
mkdir -p "$TMP_DIR/skills/dev/ancien"
|
|
cat > "$TMP_DIR/skills/dev/ancien/SKILL.md" << 'EOF'
|
|
---
|
|
name: ancien
|
|
version: 1.0.0
|
|
agents: [claude-code]
|
|
description: Skill migré en bundle
|
|
tags: [test]
|
|
---
|
|
# Ancien migré
|
|
EOF
|
|
SKILLS_LIST=()
|
|
scan_skills 2>/dev/null
|
|
count_ancien=0
|
|
for e in "${SKILLS_LIST[@]}"; do
|
|
[[ "$e" == *"|ancien|claude-code|"* ]] && (( count_ancien++ )) || true
|
|
done
|
|
assert_eq "pas de doublon bundle+legacy pour même agent" "1" "$count_ancien"
|
|
|
|
rm -f "$STATE_FILE"
|
|
rm -rf "$TMP_DIR"
|
|
unset REPO_DIR STATE_FILE SKILLS_TAG DETECTED_AGENTS
|
|
|
|
# ── Bilan ─────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "══════════════════════════════════"
|
|
printf " Résultats : %d passés, %d échoués\n" "$PASS" "$FAIL"
|
|
echo "══════════════════════════════════"
|
|
echo ""
|
|
[[ "$FAIL" -eq 0 ]]
|