Files
ai-setup/tests/test_fixes.sh
Виталий Никитенко f61f2cde84 feat(glm): switch ai-glm to GLM 5.3
glm-5.3 доступна на api.z.ai (вышла 2026-08-14, 1M контекст): opus/sonnet =
glm-5.3, haiku = glm-5.3[1m], subagent = glm-5.3, health-check ключа тоже
на 5.3. Секция OpenRouter остаётся на z-ai/glm-5.2 — там 5.3 ещё нет.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-17 06:00:11 +03:00

315 lines
14 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Copyright (c) 2026 Виталий Никитенко. MIT License.
# tests/test_fixes.sh - unit tests for code-review fixes in ai-setup.sh
# Run: bash tests/test_fixes.sh
set -euo pipefail
SCRIPT="$(cd "$(dirname "$0")/.." && pwd)/scripts/ai-setup.sh"
GLOBAL_RULES_SOURCE="$(cd "$(dirname "$0")/.." && pwd)/home-configs/GLOBAL_RULES.md"
ADD_HOOK="$(cd "$(dirname "$0")/.." && pwd)/home-configs/claude/hooks/add-account-hook.sh"
PASS=0; FAIL=0
ok() { echo "[PASS] $1"; PASS=$((PASS+1)); }
fail() { echo "[FAIL] $1"; FAIL=$((FAIL+1)); }
# Extract sections
GPT_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-gpt"/,/^GPTEOF/' "$SCRIPT")
KIMI_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-kimi"/,/^KIMIEOF/' "$SCRIPT")
GLM_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-glm"/,/^GLMEOF/' "$SCRIPT")
GEMINI_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-gemini"/,/^GEMINIEOF/' "$SCRIPT")
OPENCODE_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-opencode"/,/^OPENCODEEOF/' "$SCRIPT")
CURSOR_SECTION=$(awk '/^cat > "\$BIN_DIR\/ai-cursor"/,/^CURSOREOF/' "$SCRIPT")
HELPERS_SECTION=$(awk '/^cat > "\$HELPERS_FILE"/,/^HELPEREOF/' "$SCRIPT")
CODEX_SOUND_HOOKS_SECTION=$(awk '/^_setup_codex_sound_hooks\(\)/,/^}/' "$SCRIPT")
# ── ai-gpt: auto-install codex ────────────────────────────────────────────
test_gpt_autoinstall() {
if echo "$GPT_SECTION" | grep -q 'curl -fsSL https://chatgpt.com/codex/install.sh'; then
ok "ai-gpt: auto-installs codex via official install script"
else
fail "ai-gpt: missing codex auto-install"
fi
}
# ── ai-gpt: no proxy logic (simplified launcher) ──────────────────────────
test_gpt_no_proxy() {
if echo "$GPT_SECTION" | grep -q 'ANTHROPIC_BASE_URL'; then
fail "ai-gpt: still contains proxy logic (ANTHROPIC_BASE_URL)"
else
ok "ai-gpt: proxy logic removed (no ANTHROPIC_BASE_URL)"
fi
}
# ── ai-kimi: launches claude with Kimi backend ────────────────────────────
test_kimi_claude_launcher() {
if echo "$KIMI_SECTION" | grep -q 'claude --dangerously-skip-permissions' \
&& echo "$KIMI_SECTION" | grep -q 'ANTHROPIC_BASE_URL'; then
ok "ai-kimi: launches claude with ANTHROPIC_BASE_URL set"
else
fail "ai-kimi: must launch claude with ANTHROPIC_BASE_URL"
fi
}
# ── ai-kimi: uses official Kimi API ──────────────────────────────────────
test_kimi_official_api() {
if echo "$KIMI_SECTION" | grep -q 'api.kimi.com/coding' \
&& echo "$KIMI_SECTION" | grep -q 'ANTHROPIC_DEFAULT_OPUS_MODEL=kimi-k2.7' \
&& echo "$KIMI_SECTION" | grep -q 'ANTHROPIC_DEFAULT_HAIKU_MODEL=kimi-k2.6' \
&& ! echo "$KIMI_SECTION" | grep -q 'artemox'; then
ok "ai-kimi: uses official Kimi API (K2.7 opus/sonnet, K2.6 haiku)"
else
fail "ai-kimi: must use official Kimi API (api.kimi.com/coding) with K2.7 opus/sonnet, K2.6 haiku"
fi
}
# ── ai-kimi: no artemox or config.toml logic ─────────────────────────────
test_kimi_no_artemox() {
if echo "$KIMI_SECTION" | grep -q 'config.toml' || echo "$KIMI_SECTION" | grep -q 'artemox'; then
fail "ai-kimi: still contains Artemox or config.toml logic"
else
ok "ai-kimi: Artemox and config.toml logic removed"
fi
}
# ── ai-glm: launches claude with GLM backend ──────────────────────────────
test_glm_claude_launcher() {
if echo "$GLM_SECTION" | grep -q 'claude --dangerously-skip-permissions' \
&& echo "$GLM_SECTION" | grep -q 'ANTHROPIC_BASE_URL'; then
ok "ai-glm: launches claude with ANTHROPIC_BASE_URL set"
else
fail "ai-glm: must launch claude with ANTHROPIC_BASE_URL"
fi
}
# ── ai-glm: uses official Zhipu AI API ────────────────────────────────────
test_glm_official_api() {
if echo "$GLM_SECTION" | grep -q 'api.z.ai/api/anthropic' \
&& echo "$GLM_SECTION" | grep -q 'ANTHROPIC_DEFAULT_OPUS_MODEL=glm-5.3' \
&& echo "$GLM_SECTION" | grep -q "ANTHROPIC_DEFAULT_HAIKU_MODEL='glm-5.3\[1m\]'" \
&& echo "$GLM_SECTION" | grep -q 'x-api-key'; then
ok "ai-glm: uses official Zhipu AI API (glm-5.3 opus/sonnet, glm-5.3[1m] haiku)"
else
fail "ai-glm: must use official Zhipu AI API with glm-5.3 and x-api-key"
fi
}
# ── ai-gemini: native launcher generation ─────────────────────────────────
test_gemini_native_launcher() {
if echo "$GEMINI_SECTION" | grep -q 'antigravity CLI (agy)' \
&& echo "$GEMINI_SECTION" | grep -q 'https://antigravity.google/cli/install.sh'; then
ok "ai-gemini: native agy launcher is generated"
else
fail "ai-gemini: missing native agy launcher generation"
fi
}
# ── ai-cursor: native launcher generation ──────────────────────────────────
test_cursor_native_launcher() {
if echo "$CURSOR_SECTION" | grep -q 'Cursor CLI (agent)' \
&& echo "$CURSOR_SECTION" | grep -q 'https://cursor.com/install'; then
ok "ai-cursor: native Cursor CLI (agent) launcher is generated"
else
fail "ai-cursor: missing native Cursor CLI launcher generation"
fi
}
# ── global rules: source and native rule files ───────────────────────
test_global_rules_source_installed() {
if [ -s "$GLOBAL_RULES_SOURCE" ] \
&& grep -q 'GLOBAL_RULES_SOURCE=' "$SCRIPT" \
&& grep -q 'cp "$GLOBAL_RULES_SOURCE" "$CONFIG_DIR/global_rules.md"' "$SCRIPT"; then
ok "global rules: non-empty source file is installed as global_rules.md"
else
fail "global rules: missing source file or installation wiring"
fi
}
test_native_rule_files_generated() {
if grep -q 'cp "$CONFIG_DIR/global_rules.md" "$HOME/.codex/AGENTS.md"' "$SCRIPT" \
&& grep -q 'cp "$CONFIG_DIR/global_rules.md" "$HOME/.kimi-code/AGENTS.md"' "$SCRIPT" \
&& grep -q 'cp "$CONFIG_DIR/global_rules.md" "$HOME/.claude/CLAUDE.md"' "$SCRIPT" \
&& grep -q 'cp "$CONFIG_DIR/global_rules.md" "$HOME/.gemini/GEMINI.md"' "$SCRIPT" \
&& echo "$HELPERS_SECTION" | grep -q 'echo "$global_rendered" > "$HOME/.codex/AGENTS.md"' \
&& echo "$HELPERS_SECTION" | grep -q 'echo "$global_rendered" > "$HOME/.kimi-code/AGENTS.md"' \
&& echo "$HELPERS_SECTION" | grep -q 'echo "$global_rendered" > "$HOME/.claude/CLAUDE.md"' \
&& echo "$HELPERS_SECTION" | grep -q 'echo "$global_rendered" > "$HOME/.gemini/GEMINI.md"' \
&& ! echo "$HELPERS_SECTION" | grep -q 'echo "$rendered" >'; then
ok "global rules: setup and launchers write native rule files from global_rules.md"
else
fail "global rules: setup and launchers must write native rule files from global_rules.md"
fi
}
test_codex_sound_hooks_detach_stdio() {
local detached_count
detached_count=$(printf '%s\n' "$CODEX_SOUND_HOOKS_SECTION" | grep -Fc ') </dev/null >/dev/null 2>&1 &' || true)
if [ "$detached_count" -eq 2 ]; then
ok "codex hooks: background sound players detach standard streams"
else
fail "codex hooks: background sound players can keep hook pipes open"
fi
}
# ── Fix 7: trap quotes $TMP correctly ────────────────────────────────────────
test_fix7_trap_tmp() {
if grep -q "trap 'rm -rf \"\$TMP\"' EXIT" "$SCRIPT"; then
ok "Fix7: trap uses single quotes with quoted \"\$TMP\""
else
fail "Fix7: trap still uses double quotes or \$TMP still unquoted at execution"
fi
}
# ── bash syntax of the whole script ─────────────────────────────────────────
test_script_syntax() {
if bash -n "$SCRIPT" 2>&1; then
ok "syntax: ai-setup.sh passes 'bash -n'"
else
fail "syntax: ai-setup.sh has syntax errors"
fi
}
# ── Android CLI: install block exists ─────────────────────────────────────
test_android_cli_install_url() {
if grep -q 'dl.google.com/android/cli/latest' "$SCRIPT"; then
ok "android-cli: install URL for Android CLI is present"
else
fail "android-cli: missing install URL for Android CLI"
fi
}
test_android_init_called() {
if grep -q 'android init' "$SCRIPT"; then
ok "android-cli: android init is invoked after install"
else
fail "android-cli: missing android init invocation"
fi
}
test_android_symlink_to_bin_dir() {
if grep -q 'ln -sf "$HOME/.local/bin/android" "$BIN_DIR/android"' "$SCRIPT"; then
ok "android-cli: symlink to BIN_DIR is handled"
else
fail "android-cli: missing symlink to BIN_DIR"
fi
}
# ── ai-opencode: launches claude with routatic-proxy backend ──────────────────
test_opencode_claude_launcher() {
if echo "$OPENCODE_SECTION" | grep -q 'claude --dangerously-skip-permissions' \
&& echo "$OPENCODE_SECTION" | grep -q 'ANTHROPIC_BASE_URL'; then
ok "ai-opencode: launches claude with ANTHROPIC_BASE_URL set"
else
fail "ai-opencode: must launch claude with ANTHROPIC_BASE_URL"
fi
}
# ── ai-opencode: uses routatic-proxy (local proxy) ─────────────────────────────
test_opencode_routatic_proxy() {
if echo "$OPENCODE_SECTION" | grep -q 'routatic-proxy' \
&& echo "$OPENCODE_SECTION" | grep -q '127.0.0.1:3456' \
&& echo "$OPENCODE_SECTION" | grep -q 'ROUTATIC_PROXY_API_KEY'; then
ok "ai-opencode: uses routatic-proxy on 127.0.0.1:3456"
else
fail "ai-opencode: must use routatic-proxy with local proxy and API key env"
fi
}
# ── ai-opencode: manages opencode_key ──────────────────────────────────────────
test_opencode_key_management() {
if echo "$OPENCODE_SECTION" | grep -q 'opencode_key' \
&& echo "$OPENCODE_SECTION" | grep -q 'AI_RESET_KEY' \
&& echo "$OPENCODE_SECTION" | grep -q 'OPENCODE_GO_API_KEY'; then
ok "ai-opencode: opencode_key with AI_RESET_KEY and OPENCODE_GO_API_KEY env fallback"
else
fail "ai-opencode: must manage opencode_key with reset and env fallback"
fi
}
# ── ai-opencode: correct default models ────────────────────────────────────────
test_opencode_models() {
if echo "$OPENCODE_SECTION" | grep -q 'ANTHROPIC_DEFAULT_OPUS_MODEL="deepseek-v4-pro"' \
&& echo "$OPENCODE_SECTION" | grep -q 'ANTHROPIC_DEFAULT_HAIKU_MODEL="kimi-k2.6"' \
&& echo "$OPENCODE_SECTION" | grep -q 'CLAUDE_CODE_DISABLE_1M_CONTEXT=1'; then
ok "ai-opencode: correct default models (deepseek-v4-pro opus, kimi-k2.6 haiku)"
else
fail "ai-opencode: must have deepseek-v4-pro opus and kimi-k2.6 haiku"
fi
}
# ── add-account: логину нужен tty, фоновый запуск висит вечно ────────────────
test_add_account_no_headless_login() {
if grep -q 'claude auth login.*</dev/null' "$ADD_HOOK"; then
fail "add-account: логин запускается с закрытым stdin — код из браузера вставить некуда"
else
ok "add-account: логин не запускается в фоне с закрытым stdin"
fi
}
test_add_account_single_terminal_flow() {
if grep -q 'mkfifo' "$ADD_HOOK" \
&& grep -q 'add-account <код>' "$ADD_HOOK" \
&& ! grep -q 'gnome-terminal' "$ADD_HOOK"; then
ok "add-account: двухшаговый флоу в одном терминале, без второго окна"
else
fail "add-account: логин должен идти через fifo в текущем терминале, без отдельного окна"
fi
}
test_add_account_kills_by_pid() {
if grep -v '^[[:space:]]*#' "$ADD_HOOK" | grep -q 'pkill'; then
fail "add-account: pkill -f выкосит любой процесс с этой подстрокой в cmdline"
else
ok "add-account: процессы гасятся по своим PID, без широкого pkill -f"
fi
}
test_add_account_checks_login_result() {
if grep -q 'Login failed' "$ADD_HOOK" \
&& ! grep -q 'SAVED:' "$ADD_HOOK"; then
ok "add-account: результат логина проверяется, провал не выдаётся за успех"
else
fail "add-account: провал логина не должен маскироваться под успешное сохранение"
fi
}
test_add_hook_syntax() {
if bash -n "$ADD_HOOK" 2>&1; then
ok "syntax: add-account-hook.sh passes 'bash -n'"
else
fail "syntax: add-account-hook.sh has syntax errors"
fi
}
# ── run all tests ─────────────────────────────────────────────────────────────
test_script_syntax
test_gpt_autoinstall
test_gpt_no_proxy
test_kimi_claude_launcher
test_kimi_official_api
test_kimi_no_artemox
test_glm_claude_launcher
test_glm_official_api
test_gemini_native_launcher
test_cursor_native_launcher
test_global_rules_source_installed
test_native_rule_files_generated
test_codex_sound_hooks_detach_stdio
test_fix7_trap_tmp
test_android_cli_install_url
test_android_init_called
test_android_symlink_to_bin_dir
test_opencode_claude_launcher
test_opencode_routatic_proxy
test_opencode_key_management
test_opencode_models
test_add_hook_syntax
test_add_account_no_headless_login
test_add_account_single_terminal_flow
test_add_account_kills_by_pid
test_add_account_checks_login_result
echo ""
echo "Results: $PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ] && exit 0 || exit 1