#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../../lib/test-helpers.sh"
SKILLS_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)"
TEAM=""
TEAM_DIR=""
INIT_SCRIPT=""
PASS_COUNT=0
FAIL_COUNT=0
WARN_COUNT=0
FAKE_REPOS_CREATED=""
get_git_repo_names() {
local init="$1"
grep -E 'ln -sfn.*"\$?CONFIG_ROOT/' "$init" 2>/dev/null | sed -E 's/.*CONFIG_ROOT\/([^"[:space:]]+).*/\1/' | grep -vxE 'AGENTS.md|workflows|agents|skills' | grep -v '^skills/' | sort -u
}
setup_fake_repos() {
local team_dir="$1"
local repo
while IFS= read -r repo; do
[ -n "$repo" ] || continue
local repo_dir="$team_dir/$repo"
if [ ! -d "$repo_dir" ]; then
mkdir -p "$repo_dir/docs" "$repo_dir/examples"
touch "$repo_dir/docs/README.md"
touch "$repo_dir/examples/example.py"
touch "$repo_dir/.test-fake-repo"
FAKE_REPOS_CREATED="$FAKE_REPOS_CREATED $repo"
fi
done < <(get_git_repo_names "$team_dir/init.sh")
}
cleanup_fake_repos() {
for repo in $FAKE_REPOS_CREATED; do
if [ -f "$TEAM_DIR/$repo/.test-fake-repo" ]; then
rm -rf "$TEAM_DIR/$repo"
fi
done
FAKE_REPOS_CREATED=""
}
cleanup_team_artifacts() {
local dir="${1:-$TEAM_DIR}"
rm -rf "$dir/.opencode" "$dir/.claude" "$dir/.trae" "$dir/.marscode" "$dir/.traecli"
}
get_expected_skill_count() {
local skills
skills=$(grep 'INCLUDED_SKILLS=' "$INIT_SCRIPT" 2>/dev/null | head -1 | sed 's/.*="//;s/"$//' || true)
echo "$skills" | wc -w
}
get_expected_agent_count() {
local pattern
pattern=$(grep 'INCLUDED_AGENT_PATTERN=' "$INIT_SCRIPT" 2>/dev/null | head -1 | sed 's/.*="//;s/"$//' || true)
local count=0
for f in "$TEAM_DIR/agents/"*.md; do
[ -f "$f" ] || continue
local base
base=$(basename "$f" .md)
if [[ "$base" == $pattern ]]; then
count=$((count + 1))
fi
done
echo "$count"
}
run_check() {
local name="$1"
shift
if "$@"; then
print_pass "$name"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "$name"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
}
verify_symlinks_valid() {
local dir="$1"
local label="$2"
local broken=0
if [ -d "$dir" ]; then
for link in "$dir"/*; do
[ -e "$link" ] || [ -L "$link" ] || continue
if [ -L "$link" ]; then
local target
target=$(readlink "$link")
if [ ! -e "$target" ]; then
print_fail "$label: broken symlink '$link' → '$target'"
FAIL_COUNT=$((FAIL_COUNT + 1))
broken=$((broken + 1))
fi
fi
done
fi
if [ "$broken" -eq 0 ]; then
print_pass "$label: all symlinks resolve to existing targets"
PASS_COUNT=$((PASS_COUNT + 1))
fi
}
verify_manifest() {
local config_root="$1"
local expected_skills="$2"
local expected_agents="$3"
local manifest="$config_root/cannbot-manifest.json"
if [ ! -f "$manifest" ]; then
print_fail "manifest not found: $manifest"
FAIL_COUNT=$((FAIL_COUNT + 1))
return 1
fi
if python3 -c "import json; json.load(open('$manifest'))" 2>/dev/null; then
print_pass "manifest is valid JSON"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "manifest is invalid JSON"
FAIL_COUNT=$((FAIL_COUNT + 1))
return 1
fi
local actual_skills
actual_skills=$(python3 -c "import json; print(len(json.load(open('$manifest')).get('installed_skills', [])))" 2>/dev/null || echo 0)
if [ "$actual_skills" -eq "$expected_skills" ]; then
print_pass "manifest: installed_skills count = $actual_skills (expected $expected_skills)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "manifest: installed_skills count = $actual_skills (expected $expected_skills)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
local actual_agents
actual_agents=$(python3 -c "import json; print(len(json.load(open('$manifest')).get('installed_agents', [])))" 2>/dev/null || echo 0)
if [ "$actual_agents" -eq "$expected_agents" ]; then
print_pass "manifest: installed_agents count = $actual_agents (expected $expected_agents)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "manifest: installed_agents count = $actual_agents (expected $expected_agents)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
for field in brand level tool; do
local val
val=$(python3 -c "import json; print(json.load(open('$manifest')).get('$field',''))" 2>/dev/null || true)
if [ -n "$val" ]; then
print_pass "manifest: '$field' = '$val'"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "manifest: missing '$field' field"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done
}
verify_installed_names() {
local dir="$1"
local label="$2"
local expected_list="$3"
if [ ! -d "$dir" ]; then
print_fail "$label: directory not found"
FAIL_COUNT=$((FAIL_COUNT + 1))
return 1
fi
local mismatch=0
for item in "$dir"/*; do
[ -e "$item" ] || [ -L "$item" ] || continue
local name
name=$(basename "$item")
if ! echo "$expected_list" | grep -qw "$name"; then
print_fail "$label: unexpected item '$name' installed"
FAIL_COUNT=$((FAIL_COUNT + 1))
mismatch=$((mismatch + 1))
fi
done
if [ "$mismatch" -eq 0 ]; then
print_pass "$label: no unexpected items installed"
PASS_COUNT=$((PASS_COUNT + 1))
fi
}
check_common_artifacts() {
local config_root="$1"
local tool="$2"
run_check "CONFIG_ROOT exists: $config_root" test -d "$config_root"
local skill_dir="$config_root/skills"
if [ -d "$skill_dir" ]; then
local actual_skills
actual_skills=$(find "$skill_dir" -maxdepth 1 \( -type l -o -type d \) | wc -l)
actual_skills=$((actual_skills - 1))
if [ "$actual_skills" -eq "$EXPECTED_SKILL_COUNT" ]; then
print_pass "skills/ contains $actual_skills item(s) (expected $EXPECTED_SKILL_COUNT)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "skills/ contains $actual_skills item(s) (expected $EXPECTED_SKILL_COUNT)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
else
print_fail "skills/ directory not found"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
local agent_dir="$config_root/agents"
if [ -d "$agent_dir" ]; then
local actual_agents
actual_agents=$(find "$agent_dir" -maxdepth 1 -type l | wc -l)
if [ "$actual_agents" -eq "$EXPECTED_AGENT_COUNT" ]; then
print_pass "agents/ contains $actual_agents symlink(s) (expected $EXPECTED_AGENT_COUNT)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "agents/ contains $actual_agents symlink(s) (expected $EXPECTED_AGENT_COUNT)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
else
print_fail "agents/ directory not found"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
verify_symlinks_valid "$skill_dir" "skills/"
verify_symlinks_valid "$agent_dir" "agents/"
verify_installed_names "$skill_dir" "skills/" "$EXPECTED_SKILLS_LIST"
if [ -d "$TEAM_DIR/workflows" ]; then
local wf_link="$config_root/workflows"
if [ -L "$wf_link" ]; then
local wf_target
wf_target=$(readlink "$wf_link")
if [ -d "$wf_target" ]; then
print_pass "workflows symlink resolves to existing directory"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "workflows symlink target does not exist: $wf_target"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
else
print_fail "workflows symlink not found"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
fi
verify_manifest "$config_root" "$EXPECTED_SKILL_COUNT" "$EXPECTED_AGENT_COUNT"
}
verify_opencode_cli_agents() {
local scan_dir="${1:-$TEAM_DIR}"
if ! command -v opencode &>/dev/null; then
print_skip "opencode CLI not available, skipping CLI agent recognition check"
return 0
fi
local pattern
pattern=$(grep 'INCLUDED_AGENT_PATTERN=' "$INIT_SCRIPT" | head -1 | sed 's/.*="//;s/"$//')
local expected=()
for f in "$TEAM_DIR/agents/"*.md; do
[ -f "$f" ] || continue
local base
base=$(basename "$f" .md)
if [[ "$base" == $pattern ]]; then
expected+=("$base")
fi
done
if [ ${#expected[@]} -eq 0 ]; then
print_info "No agents match pattern for CLI check"
return 0
fi
local output
output=$(cd "$scan_dir" && opencode agent list 2>&1 || true)
for agent in "${expected[@]}"; do
if echo "$output" | grep -q "^$agent "; then
print_pass "opencode CLI recognizes agent: $agent"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "opencode CLI does NOT recognize agent: $agent"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done
}
verify_opencode_discovery() {
local config_root="$1"
local found=0
local fail=0
local agent_dir="$config_root/agents"
found=0; fail=0
for link in "$agent_dir"/*; do
[ -e "$link" ] || [ -L "$link" ] || continue
local name
name=$(basename "$link")
if [ -f "$link" ]; then
found=$((found + 1))
else
print_fail "OpenCode scan: agent '$name' → not a readable file"
FAIL_COUNT=$((FAIL_COUNT + 1))
fail=$((fail + 1))
fi
done
if [ "$fail" -eq 0 ]; then
print_pass "OpenCode scan: all $found agents are readable .md files"
PASS_COUNT=$((PASS_COUNT + 1))
fi
}
verify_claude_discovery() {
local config_root="$1"
local level="$2"
local tmp_pwd="$3"
local found=0
local fail=0
local agent_dir="$config_root/agents"
found=0; fail=0
for link in "$agent_dir"/*; do
[ -e "$link" ] || [ -L "$link" ] || continue
local name
name=$(basename "$link")
if [ -f "$link" ]; then
found=$((found + 1))
else
print_fail "Claude scan: agent '$name' → not a readable file"
FAIL_COUNT=$((FAIL_COUNT + 1))
fail=$((fail + 1))
fi
done
if [ "$fail" -eq 0 ]; then
print_pass "Claude scan: all $found agents are readable .md files"
PASS_COUNT=$((PASS_COUNT + 1))
fi
local claude_md
if [ "$level" = "project" ]; then
claude_md="$tmp_pwd/CLAUDE.md"
else
claude_md="$config_root/CLAUDE.md"
fi
if [ -f "$claude_md" ] && [ -s "$claude_md" ]; then
print_pass "Claude scan: CLAUDE.md in project root is present and non-empty"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "Claude scan: CLAUDE.md in project root missing or empty"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
}
scenario_project_opencode() {
print_section_header "Scenario: project + opencode"
local tmp_home tmp_pwd
tmp_home=$(mktemp -d)
tmp_pwd=$(mktemp -d)
trap "rm -rf '$tmp_home' '$tmp_pwd'; cleanup_team_artifacts '$tmp_pwd'" EXIT
setup_fake_repos "$TEAM_DIR"
cleanup_team_artifacts "$tmp_pwd"
local output
local exit_code=0
output=$(cd "$tmp_pwd" && HOME="$tmp_home" bash "$INIT_SCRIPT" project opencode <<< "y" 2>&1) || exit_code=$?
if [ "$exit_code" -eq 0 ]; then
print_pass "init.sh exited with code 0"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "init.sh exited with code $exit_code"
echo "$output" | tail -20
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
local config_root="$tmp_pwd/.opencode"
check_common_artifacts "$config_root" "opencode"
if [ -e "$tmp_pwd/AGENTS.md" ]; then
print_pass "PWD/AGENTS.md exists"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "PWD/AGENTS.md is missing"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
if [ -f "$tmp_pwd/AGENTS.md" ] && [ ! -L "$tmp_pwd/AGENTS.md" ] && grep -q "$TEAM_DIR" "$tmp_pwd/AGENTS.md" 2>/dev/null; then
print_pass "PWD/AGENTS.md contains absolute paths (project mode rewrite)"
PASS_COUNT=$((PASS_COUNT + 1))
elif [ -L "$tmp_pwd/AGENTS.md" ]; then
print_pass "PWD/AGENTS.md is a symlink (plugin dir = PWD)"
PASS_COUNT=$((PASS_COUNT + 1))
fi
local repo
while IFS= read -r repo; do
[ -n "$repo" ] || continue
if [ ! -e "$config_root/$repo" ]; then
print_pass "$repo not symlinked into CONFIG_ROOT (project mode, correct)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "$repo unexpectedly present in CONFIG_ROOT (project mode)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done < <(get_git_repo_names "$INIT_SCRIPT")
verify_opencode_discovery "$config_root"
verify_opencode_cli_agents "$tmp_pwd"
rm -rf "$tmp_home" "$tmp_pwd"
cleanup_team_artifacts "$tmp_pwd"
trap - EXIT
}
scenario_global_opencode() {
print_section_header "Scenario: global + opencode"
local tmp_home
tmp_home=$(mktemp -d)
trap "rm -rf '$tmp_home'; cleanup_team_artifacts" EXIT
setup_fake_repos "$TEAM_DIR"
cleanup_team_artifacts
local output
local exit_code=0
output=$(HOME="$tmp_home" bash "$INIT_SCRIPT" global opencode <<< "y" 2>&1) || exit_code=$?
if [ "$exit_code" -eq 0 ]; then
print_pass "init.sh exited with code 0"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "init.sh exited with code $exit_code"
echo "$output" | tail -20
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
local config_root="$tmp_home/.config/opencode"
check_common_artifacts "$config_root" "opencode"
if grep -q 'ESCAPED_ROOT' "$INIT_SCRIPT" 2>/dev/null; then
local config_file="$config_root/AGENTS.md"
if [ -f "$config_file" ] && [ ! -L "$config_file" ]; then
print_pass "AGENTS.md is a regular file (global mode copy, not symlink)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "AGENTS.md is missing or is a symlink (expected regular file in global mode)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
if grep -q "$TEAM_DIR" "$config_file" 2>/dev/null; then
print_pass "AGENTS.md contains absolute paths (global mode rewrite detected)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_warn "AGENTS.md does not contain absolute paths (may be OK if no relative refs)"
WARN_COUNT=$((WARN_COUNT + 1))
fi
fi
local repo
while IFS= read -r repo; do
[ -n "$repo" ] || continue
if [ -L "$config_root/$repo" ]; then
print_pass "$repo symlinked into CONFIG_ROOT (global mode, correct)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "$repo NOT symlinked into CONFIG_ROOT (global mode)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done < <(get_git_repo_names "$INIT_SCRIPT")
verify_opencode_discovery "$config_root"
rm -rf "$tmp_home"
cleanup_team_artifacts
trap - EXIT
}
scenario_project_claude() {
print_section_header "Scenario: project + claude"
local tmp_home tmp_pwd
tmp_home=$(mktemp -d)
tmp_pwd=$(mktemp -d)
trap "rm -rf '$tmp_home' '$tmp_pwd'; cleanup_team_artifacts '$tmp_pwd'" EXIT
setup_fake_repos "$TEAM_DIR"
cleanup_team_artifacts "$tmp_pwd"
local output
local exit_code=0
output=$(cd "$tmp_pwd" && HOME="$tmp_home" bash "$INIT_SCRIPT" project claude <<< "y" 2>&1) || exit_code=$?
if [ "$exit_code" -eq 0 ]; then
print_pass "init.sh exited with code 0"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "init.sh exited with code $exit_code"
echo "$output" | tail -20
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
local config_root="$tmp_pwd/.claude"
check_common_artifacts "$config_root" "claude"
local claude_md="$tmp_pwd/CLAUDE.md"
if [ -e "$claude_md" ]; then
print_pass "CLAUDE.md in project root exists"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "CLAUDE.md in project root is missing"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
if [ -f "$claude_md" ] && [ ! -L "$claude_md" ] && grep -q "$TEAM_DIR" "$claude_md" 2>/dev/null; then
print_pass "CLAUDE.md contains absolute paths (project mode rewrite)"
PASS_COUNT=$((PASS_COUNT + 1))
elif [ -L "$claude_md" ]; then
print_pass "CLAUDE.md is a symlink (plugin dir = PWD)"
PASS_COUNT=$((PASS_COUNT + 1))
fi
local repo
while IFS= read -r repo; do
[ -n "$repo" ] || continue
if [ ! -e "$config_root/$repo" ]; then
print_pass "$repo not symlinked into CONFIG_ROOT (project mode, correct)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "$repo unexpectedly present in CONFIG_ROOT (project mode)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done < <(get_git_repo_names "$INIT_SCRIPT")
verify_claude_discovery "$config_root" "project" "$tmp_pwd"
rm -rf "$tmp_home" "$tmp_pwd"
cleanup_team_artifacts "$tmp_pwd"
trap - EXIT
}
scenario_global_claude() {
print_section_header "Scenario: global + claude"
local tmp_home
tmp_home=$(mktemp -d)
trap "rm -rf '$tmp_home'; cleanup_team_artifacts" EXIT
setup_fake_repos "$TEAM_DIR"
cleanup_team_artifacts
local output
local exit_code=0
output=$(HOME="$tmp_home" bash "$INIT_SCRIPT" global claude <<< "y" 2>&1) || exit_code=$?
if [ "$exit_code" -eq 0 ]; then
print_pass "init.sh exited with code 0"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "init.sh exited with code $exit_code"
echo "$output" | tail -20
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
local config_root="$tmp_home/.claude"
check_common_artifacts "$config_root" "claude"
if grep -q 'ESCAPED_ROOT' "$INIT_SCRIPT" 2>/dev/null; then
local config_file="$config_root/CLAUDE.md"
if [ -f "$config_file" ] && [ ! -L "$config_file" ]; then
print_pass "CLAUDE.md is a regular file (global mode copy, not symlink)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "CLAUDE.md is missing or is a symlink (expected regular file in global mode)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
if grep -q "$TEAM_DIR" "$config_file" 2>/dev/null; then
print_pass "CLAUDE.md contains absolute paths (global mode rewrite detected)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_warn "CLAUDE.md does not contain absolute paths (may be OK if no relative refs)"
WARN_COUNT=$((WARN_COUNT + 1))
fi
fi
local repo
while IFS= read -r repo; do
[ -n "$repo" ] || continue
if [ -L "$config_root/$repo" ]; then
print_pass "$repo symlinked into CONFIG_ROOT (global mode, correct)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "$repo NOT symlinked into CONFIG_ROOT (global mode)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done < <(get_git_repo_names "$INIT_SCRIPT")
verify_claude_discovery "$config_root" "global" ""
rm -rf "$tmp_home"
cleanup_team_artifacts
trap - EXIT
}
scenario_project_trae_ide() {
print_section_header "Scenario: project + trae (IDE path)"
local tmp_home tmp_pwd
tmp_home=$(mktemp -d)
tmp_pwd=$(mktemp -d)
trap "rm -rf '$tmp_home' '$tmp_pwd'; cleanup_team_artifacts '$tmp_pwd'" EXIT
setup_fake_repos "$TEAM_DIR"
cleanup_team_artifacts "$tmp_pwd"
mkdir -p "$tmp_home/.trae"
local output
local exit_code=0
output=$(cd "$tmp_pwd" && HOME="$tmp_home" bash "$INIT_SCRIPT" project trae <<< "y" 2>&1) || exit_code=$?
if [ "$exit_code" -eq 0 ]; then
print_pass "init.sh exited with code 0"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "init.sh exited with code $exit_code"
echo "$output" | tail -20
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
local config_root="$tmp_pwd/.trae"
if [ -d "$config_root" ]; then
print_pass "Artifacts installed to .trae/ (IDE path detected)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail ".trae/ directory not found after IDE-path installation"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
if echo "$output" | grep -q "Detected: TRAE IDE"; then
print_pass "Output contains TRAE IDE detection message"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_warn "Output missing TRAE IDE detection message"
WARN_COUNT=$((WARN_COUNT + 1))
fi
check_common_artifacts "$config_root" "trae"
rm -rf "$tmp_home" "$tmp_pwd"
cleanup_team_artifacts "$tmp_pwd"
trap - EXIT
}
scenario_project_trae_plugin() {
print_section_header "Scenario: project + trae (Plugin path)"
local tmp_home tmp_pwd
tmp_home=$(mktemp -d)
tmp_pwd=$(mktemp -d)
trap "rm -rf '$tmp_home' '$tmp_pwd'; cleanup_team_artifacts '$tmp_pwd'" EXIT
setup_fake_repos "$TEAM_DIR"
cleanup_team_artifacts "$tmp_pwd"
mkdir -p "$tmp_home/.marscode"
local output
local exit_code=0
output=$(cd "$tmp_pwd" && HOME="$tmp_home" bash "$INIT_SCRIPT" project trae <<< "y" 2>&1) || exit_code=$?
if [ "$exit_code" -eq 0 ]; then
print_pass "init.sh exited with code 0"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "init.sh exited with code $exit_code"
echo "$output" | tail -20
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
local config_root="$tmp_pwd/.marscode"
if [ -d "$config_root" ]; then
print_pass "Artifacts installed to .marscode/ (Plugin path detected)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail ".marscode/ directory not found after Plugin-path installation"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
if echo "$output" | grep -q "Detected: TRAE Plugin"; then
print_pass "Output contains TRAE Plugin detection message"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_warn "Output missing TRAE Plugin detection message"
WARN_COUNT=$((WARN_COUNT + 1))
fi
check_common_artifacts "$config_root" "trae"
rm -rf "$tmp_home" "$tmp_pwd"
cleanup_team_artifacts "$tmp_pwd"
trap - EXIT
}
scenario_project_trae_cli() {
print_section_header "Scenario: project + trae (CLI path)"
local tmp_home tmp_pwd
tmp_home=$(mktemp -d)
tmp_pwd=$(mktemp -d)
trap "rm -rf '$tmp_home' '$tmp_pwd'; cleanup_team_artifacts '$tmp_pwd'" EXIT
setup_fake_repos "$TEAM_DIR"
cleanup_team_artifacts "$tmp_pwd"
mkdir -p "$tmp_home/.traecli"
local output
local exit_code=0
output=$(cd "$tmp_pwd" && HOME="$tmp_home" bash "$INIT_SCRIPT" project trae <<< "y" 2>&1) || exit_code=$?
if [ "$exit_code" -eq 0 ]; then
print_pass "init.sh exited with code 0"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail "init.sh exited with code $exit_code"
echo "$output" | tail -20
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
local config_root="$tmp_pwd/.traecli"
if [ -d "$config_root" ]; then
print_pass "Artifacts installed to .traecli/ (CLI path detected)"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_fail ".traecli/ directory not found after CLI-path installation"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
if echo "$output" | grep -q "Detected: TRAE CLI"; then
print_pass "Output contains TRAE CLI detection message"
PASS_COUNT=$((PASS_COUNT + 1))
else
print_warn "Output missing TRAE CLI detection message"
WARN_COUNT=$((WARN_COUNT + 1))
fi
check_common_artifacts "$config_root" "trae"
rm -rf "$tmp_home" "$tmp_pwd"
cleanup_team_artifacts "$tmp_pwd"
trap - EXIT
}
main() {
echo "========================================"
echo " Behavior Test: Init Script Install"
echo "========================================"
echo ""
local teams=()
for team_dir in "$SKILLS_DIR/plugins-official"/*; do
[ -d "$team_dir" ] || continue
local init="$team_dir/init.sh"
[ -f "$init" ] || continue
[ -x "$init" ] || continue
teams+=("$(basename "$team_dir")")
done
if [ ${#teams[@]} -eq 0 ]; then
print_fail "No teams with init.sh found"
exit 1
fi
echo "Teams to test: ${teams[*]}"
echo ""
for team in "${teams[@]}"; do
TEAM="$team"
TEAM_DIR="$SKILLS_DIR/plugins-official/$TEAM"
INIT_SCRIPT="$TEAM_DIR/init.sh"
EXPECTED_SKILL_COUNT=$(get_expected_skill_count)
EXPECTED_AGENT_COUNT=$(get_expected_agent_count)
EXPECTED_SKILLS_LIST=$(grep 'INCLUDED_SKILLS=' "$INIT_SCRIPT" 2>/dev/null | head -1 | sed 's/.*="//;s/"$//' || true)
echo "========================================"
echo " Team: $TEAM"
echo " Expected skills: $EXPECTED_SKILL_COUNT"
echo " Expected agents: $EXPECTED_AGENT_COUNT"
echo "========================================"
echo ""
if [ ! -f "$INIT_SCRIPT" ]; then
print_fail "init.sh not found: $INIT_SCRIPT"
continue
fi
if [ ! -x "$INIT_SCRIPT" ]; then
print_fail "init.sh not executable: $INIT_SCRIPT"
continue
fi
cleanup_team_artifacts
cleanup_fake_repos
scenario_project_opencode
scenario_global_opencode
scenario_project_claude
scenario_global_claude
scenario_project_trae_ide
scenario_project_trae_plugin
scenario_project_trae_cli
cleanup_team_artifacts
cleanup_fake_repos
done
echo ""
echo "========================================"
echo " Init Behavior Test Summary"
echo "========================================"
echo " Teams: ${#teams[@]}"
echo " Passed: $PASS_COUNT"
echo " Failed: $FAIL_COUNT"
echo ""
if [ "$FAIL_COUNT" -gt 0 ]; then
print_status_failed
exit 1
else
print_status_passed
exit 0
fi
}
main "$@"