Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions test/smoke-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ if [ -x "$IDSTACK_DIR/test/test-manifest-merge.sh" ]; then
check "manifest-merge unit tests pass" "'$IDSTACK_DIR/test/test-manifest-merge.sh'"
fi

# Gen-skills tool unit tests must pass.
if [ -x "$IDSTACK_DIR/test/test-gen-skills.sh" ]; then
check "gen-skills unit tests pass" "'$IDSTACK_DIR/test/test-gen-skills.sh'"
fi

# Version classifier (shared by setup + bin/idstack-doctor) must classify
# multi-digit versions correctly. Pinned to catch the pattern-fragility
# regression Gemini flagged twice.
Expand Down
120 changes: 120 additions & 0 deletions test/test-gen-skills.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/usr/bin/env bash
# Unit tests for bin/idstack-gen-skills.
# Run from the repo root (or sourced by smoke-test.sh).

set -e

PASS=0
FAIL=0
TOTAL=0

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
GEN_SKILLS="$REPO_ROOT/bin/idstack-gen-skills"

assert() {
TOTAL=$((TOTAL + 1))
if eval "$2" >/dev/null 2>&1; then
PASS=$((PASS + 1))
echo " PASS: $1"
else
FAIL=$((FAIL + 1))
echo " FAIL: $1"
fi
}

if [ ! -x "$GEN_SKILLS" ]; then
echo "test-gen-skills: $GEN_SKILLS missing or not executable"
exit 1
fi

WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT

seed_workspace() {
# Clean existing items before seeding to avoid permission issues or stale files
rm -rf "$WORK"/* "$WORK"/.??* 2>/dev/null || true
mkdir -p "$WORK/bin" "$WORK/templates/assets" "$WORK/skills/test-skill"
Comment on lines +34 to +36

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

⚠️ Safety & Correctness Issue

Using rm -rf "$WORK"/* poses two significant issues:

  1. Severe Safety Risk: If $WORK is ever empty or unset, this command expands to rm -rf /*, which will attempt to delete the entire root filesystem.
  2. Incomplete Dotfile Cleanup: The pattern .??* only matches dotfiles with at least two characters after the dot (e.g., .ab), leaving single-character dotfiles (like .a) untouched.

πŸ’‘ Recommendation

Simply delete and recreate the entire $WORK directory. This is completely safe from the rm -rf /* expansion risk, automatically cleans up all files (including all dotfiles), and is much simpler.

Suggested change
# Clean existing items before seeding to avoid permission issues or stale files
rm -rf "$WORK"/* "$WORK"/.??* 2>/dev/null || true
mkdir -p "$WORK/bin" "$WORK/templates/assets" "$WORK/skills/test-skill"
# Clean existing items before seeding to avoid permission issues or stale files
rm -rf "$WORK"
mkdir -p "$WORK/bin" "$WORK/templates/assets" "$WORK/skills/test-skill"

cp "$GEN_SKILLS" "$WORK/bin/"

echo "PREAMBLE CONTENT" > "$WORK/templates/preamble.md"
echo "SCHEMA CONTENT" > "$WORK/templates/manifest-schema.md"
touch "$WORK/templates/report.html.tmpl"
touch "$WORK/templates/index.html.tmpl"
touch "$WORK/templates/assets/idstack.css"
echo "AGENT CONTEXT" > "$WORK/templates/agent-context.md"

cat > "$WORK/skills/test-skill/SKILL.md.tmpl" <<'INNER_EOF'
---
name: test-skill
allowed-tools:
- tool1
- tool2
description: test
---
{{PREAMBLE}}
some content
{{MANIFEST_SCHEMA}}
INNER_EOF
}

echo "test-gen-skills"
echo ""

# --- Test 1: Missing templates fail ---
seed_workspace
rm "$WORK/templates/preamble.md"
set +e
"$WORK/bin/idstack-gen-skills" --target claude >/dev/null 2>&1
EC=$?
set -e
assert "missing template exits 1" "[ $EC -eq 1 ]"

# --- Test 2: claude target generation ---
seed_workspace
"$WORK/bin/idstack-gen-skills" --target claude >/dev/null 2>&1
assert "claude target creates SKILL.md" "[ -f \"$WORK/skills/test-skill/SKILL.md\" ]"
assert "claude target substitutes PREAMBLE" "grep -q 'PREAMBLE CONTENT' \"$WORK/skills/test-skill/SKILL.md\""
assert "claude target substitutes MANIFEST_SCHEMA" "grep -q 'SCHEMA CONTENT' \"$WORK/skills/test-skill/SKILL.md\""
assert "claude target preserves allowed-tools" "grep -q 'allowed-tools:' \"$WORK/skills/test-skill/SKILL.md\""
assert "claude target injects auto-generated header" "grep -q 'AUTO-GENERATED' \"$WORK/skills/test-skill/SKILL.md\""
assert "codex target NOT created" "[ ! -f \"$WORK/dist/codex/skills/idstack-test-skill/SKILL.md\" ]"
assert "AGENTS.md NOT created" "[ ! -f \"$WORK/AGENTS.md\" ]"

# --- Test 3: codex target generation ---
seed_workspace
"$WORK/bin/idstack-gen-skills" --target codex >/dev/null 2>&1
assert "codex target creates SKILL.md" "[ -f \"$WORK/dist/codex/skills/idstack-test-skill/SKILL.md\" ]"
assert "codex target substitutes PREAMBLE" "grep -q 'PREAMBLE CONTENT' \"$WORK/dist/codex/skills/idstack-test-skill/SKILL.md\""
assert "codex target strips allowed-tools" "! grep -q 'allowed-tools:' \"$WORK/dist/codex/skills/idstack-test-skill/SKILL.md\""
assert "AGENTS.md is created" "[ -f \"$WORK/AGENTS.md\" ]"
assert "claude target NOT created" "[ ! -f \"$WORK/skills/test-skill/SKILL.md\" ]"

# --- Test 4: all target generation ---
seed_workspace
"$WORK/bin/idstack-gen-skills" >/dev/null 2>&1
assert "all target creates claude SKILL.md" "[ -f \"$WORK/skills/test-skill/SKILL.md\" ]"
assert "all target creates codex SKILL.md" "[ -f \"$WORK/dist/codex/skills/idstack-test-skill/SKILL.md\" ]"
assert "all target creates AGENTS.md" "[ -f \"$WORK/AGENTS.md\" ]"

# --- Test 5: --dry-run behavior ---
seed_workspace
"$WORK/bin/idstack-gen-skills" --target claude >/dev/null 2>&1
assert "dry-run passes when fresh" "$WORK/bin/idstack-gen-skills --dry-run --target claude >/dev/null 2>&1"
echo "stale" >> "$WORK/skills/test-skill/SKILL.md"
set +e
"$WORK/bin/idstack-gen-skills" --dry-run --target claude >/dev/null 2>&1
EC=$?
set -e
assert "dry-run exits 1 when stale" "[ $EC -eq 1 ]"

# --- Test 6: Invalid target ---
seed_workspace
set +e
"$WORK/bin/idstack-gen-skills" --target bogus >/dev/null 2>&1
EC=$?
set -e
assert "invalid target exits 2" "[ $EC -eq 2 ]"

echo ""
echo "test-gen-skills: $PASS/$TOTAL passed, $FAIL failed"
[ "$FAIL" -eq 0 ] && exit 0 || exit 1