Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
154 lines
6.8 KiB
Bash
Executable File
154 lines
6.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
source "$(dirname -- "${BASH_SOURCE[0]}")/base-test.sh"
|
|
|
|
require_command jq
|
|
require_command python3
|
|
|
|
export OMARCHY_PATH="$ROOT"
|
|
export PATH="$ROOT/bin:$PATH"
|
|
|
|
TEST_HOME=$(mktemp -d)
|
|
MOCK_PID=""
|
|
cleanup() {
|
|
[[ -n $MOCK_PID ]] && kill "$MOCK_PID" 2>/dev/null
|
|
rm -rf "$TEST_HOME"
|
|
}
|
|
trap cleanup EXIT
|
|
export HOME="$TEST_HOME"
|
|
|
|
# Force the file backend: production Secret Service / pass must never be touched
|
|
mkdir -p "$TEST_HOME/stubs"
|
|
printf '#!/bin/bash
|
|
exit 1
|
|
' > "$TEST_HOME/stubs/secret-tool"
|
|
printf '#!/bin/bash
|
|
exit 1
|
|
' > "$TEST_HOME/stubs/pass"
|
|
chmod +x "$TEST_HOME/stubs/secret-tool" "$TEST_HOME/stubs/pass"
|
|
export PATH="$TEST_HOME/stubs:$PATH"
|
|
|
|
# Registries parse and carry the P0 providers and harnesses
|
|
for reg in ai-providers ai-harnesses ai-compatibility; do
|
|
jq -e . "$ROOT/cn/registry/$reg.json" > /dev/null || fail "$reg.json parses"
|
|
done
|
|
for p in deepseek kimi zai; do
|
|
jq -e --arg p "$p" '.providers[$p].endpoints.anthropic // .providers[$p].endpoints.openai' \
|
|
"$ROOT/cn/registry/ai-providers.json" > /dev/null || fail "provider $p has an endpoint"
|
|
done
|
|
pass "AI registries parse with P0 providers"
|
|
|
|
# Secret file backend roundtrip with 0600
|
|
echo "sk-secret-1" | omarchy-cn-ai-secret set deepseek > /dev/null
|
|
[[ $(omarchy-cn-ai-secret get deepseek) == "sk-secret-1" ]] || fail "secret roundtrip"
|
|
[[ $(stat -c %a "$HOME/.local/state/omarchycn/secrets/deepseek") == 600 ]] || fail "secret file 0600"
|
|
pass "secret backend file fallback stores and returns the key at 0600"
|
|
|
|
# Profile validation matrix
|
|
omarchy-cn-ai-profile-create good claude-code deepseek default-coding > /dev/null
|
|
omarchy-cn-ai-profile-create cdx codex deepseek deepseek-v4-flash > /dev/null
|
|
omarchy-cn-ai-profile-create ock opencode deepseek default-coding > /dev/null
|
|
if omarchy-cn-ai-profile-create bad codex kimi fast > /dev/null 2>&1; then
|
|
fail "unsupported combo rejected"
|
|
fi
|
|
if omarchy-cn-ai-profile-create bad claude-code kimi kimi-k2.6 > /dev/null 2>&1; then
|
|
fail "protocol-mismatched model rejected"
|
|
fi
|
|
if omarchy-cn-ai-profile-create bad codex deepseek "deepseek-v4-pro[1m]" > /dev/null 2>&1; then
|
|
fail "codex rejects provider-namespace slug"
|
|
fi
|
|
pass "profile validation enforces combos, protocols, and allowlists"
|
|
|
|
# claude-code env render for all three providers, exact endpoints
|
|
source "$ROOT/cn/lib/ai.sh"
|
|
declare -A bases=(
|
|
[deepseek]="https://api.deepseek.com/anthropic"
|
|
[kimi]="https://api.kimi.com/coding/"
|
|
[zai]="https://api.z.ai/api/anthropic"
|
|
)
|
|
for p in deepseek kimi zai; do
|
|
out=$(cn_ai_render_env claude-code "$p" test-model sk-x)
|
|
grep -qF "ANTHROPIC_BASE_URL=${bases[$p]}" <<<"$out" || fail "claude-code $p base url" "$out"
|
|
grep -q "ANTHROPIC_AUTH_TOKEN=sk-x" <<<"$out" || fail "claude-code $p auth token"
|
|
grep -q "ANTHROPIC_API_KEY=sk-x" <<<"$out" || fail "claude-code $p api key"
|
|
done
|
|
out=$(cn_ai_render_env opencode deepseek test-model sk-x)
|
|
grep -q "DEEPSEEK_API_KEY=sk-x" <<<"$out" || fail "opencode deepseek env"
|
|
if cn_ai_render_env opencode zai m k > /dev/null 2>&1; then
|
|
fail "opencode zai refused"
|
|
fi
|
|
pass "env adapters render official endpoints for every stable combo"
|
|
|
|
# codex config render: official contract, user content preserved, idempotent
|
|
mkdir -p "$HOME/.codex"
|
|
printf '[mcp_servers.x]\ncommand = "npx"\n' > "$HOME/.codex/config.toml"
|
|
cn_ai_render_codex_config deepseek deepseek-v4-flash sk-x
|
|
for want in 'model = "deepseek-v4-flash"' 'model_catalog_json = "~/.codex/models.json"' \
|
|
'preferred_auth_method = "apikey"' 'forced_login_method = "api"' \
|
|
'wire_api = "responses"' '[model_providers.deepseek]'; do
|
|
grep -qF "$want" "$HOME/.codex/config.toml" || fail "codex contract: $want"
|
|
done
|
|
grep -q "mcp_servers.x" "$HOME/.codex/config.toml" || fail "codex user section preserved"
|
|
[[ -f $HOME/.codex/models.json ]] || fail "codex models catalog installed"
|
|
cn_ai_render_codex_config deepseek deepseek-v4-flash sk-x
|
|
[[ $(grep -c "OmarchyCN ai model begin" "$HOME/.codex/config.toml") == 1 ]] || fail "codex render idempotent"
|
|
pass "codex adapter implements the official DeepSeek contract"
|
|
|
|
# Mock server regression: anthropic 200/401 and codex responses path
|
|
port=$(python3 -c 'import socket; s = socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()')
|
|
python3 - "$port" <<'PYEOF' &
|
|
import http.server, sys
|
|
class H(http.server.BaseHTTPRequestHandler):
|
|
def do_POST(self):
|
|
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
|
|
key = self.headers.get("x-api-key") or (self.headers.get("Authorization") or "").replace("Bearer ", "")
|
|
if key != "sk-secret-1":
|
|
code = 401
|
|
elif self.path == "/v1/messages" and b"messages" in body and self.headers.get("anthropic-version"):
|
|
code = 200
|
|
elif self.path == "/responses" and b"input" in body and b"messages" not in body:
|
|
code = 200
|
|
elif self.path == "/chat/completions" and b"messages" in body:
|
|
code = 200
|
|
else:
|
|
code = 404
|
|
self.send_response(code)
|
|
self.send_header("content-length", "2")
|
|
self.end_headers()
|
|
self.wfile.write(b"{}")
|
|
def log_message(self, *a):
|
|
pass
|
|
http.server.HTTPServer(("127.0.0.1", int(sys.argv[1])), H).serve_forever()
|
|
PYEOF
|
|
MOCK_PID=$!
|
|
for _ in $(seq 1 20); do
|
|
curl -sf -o /dev/null -X POST "http://127.0.0.1:$port/v1/messages" -H "x-api-key: sk-secret-1" && break
|
|
sleep 0.2
|
|
done
|
|
|
|
OMARCHYCN_AI_TEST_BASE_URL="http://127.0.0.1:$port" omarchy-cn-ai-test good | grep -q "PASS" || fail "anthropic mock 200"
|
|
OMARCHYCN_AI_TEST_BASE_URL="http://127.0.0.1:$port" omarchy-cn-ai-test cdx | grep -q "PASS" || fail "codex responses mock 200"
|
|
echo "wrong-key" | omarchy-cn-ai-secret set deepseek > /dev/null
|
|
if OMARCHYCN_AI_TEST_BASE_URL="http://127.0.0.1:$port" omarchy-cn-ai-test good > /dev/null 2>&1; then
|
|
fail "anthropic mock 401 fails the test"
|
|
fi
|
|
pass "mock regression: 200 pass, 401 fail, responses path exercised"
|
|
|
|
# ai-default merges into claude settings without clobbering user config
|
|
printf '#!/bin/bash
|
|
exit 0
|
|
' > "$TEST_HOME/stubs/claude"
|
|
chmod +x "$TEST_HOME/stubs/claude"
|
|
echo "sk-secret-1" | omarchy-cn-ai-secret set deepseek > /dev/null
|
|
mkdir -p "$HOME/.claude"
|
|
printf '{"permissions":{"allow":["Bash"]},"env":{"KEEP":"1"}}' > "$HOME/.claude/settings.json"
|
|
omarchy-cn-ai-default good > /dev/null
|
|
[[ $(jq -r '.env.KEEP' "$HOME/.claude/settings.json") == 1 ]] || fail "ai-default preserves user env"
|
|
[[ $(jq -r '.permissions.allow[0]' "$HOME/.claude/settings.json") == Bash ]] || fail "ai-default preserves permissions"
|
|
[[ $(jq -r '.env.ANTHROPIC_BASE_URL' "$HOME/.claude/settings.json") == "https://api.deepseek.com/anthropic" ]] || fail "ai-default env written"
|
|
[[ $(cat "$HOME/.config/omarchy/defaults/agent") == claude ]] || fail "upstream default agent set"
|
|
[[ $(stat -c %a "$HOME/.claude/settings.json") == 600 ]] || fail "claude settings 0600"
|
|
pass "ai-default maps the profile onto the upstream agent entry"
|