@esankhan3/code-search-mcp 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env bash
2
+ # code-search-mcp end-to-end smoke (T1–T8 from the standalone product plan).
3
+ #
4
+ # Runs the same eight checks we used while landing the P0–P8 work. Requires:
5
+ # - Ollama running on $OLLAMA_HOST (default http://localhost:11434) with
6
+ # `bge-m3` and `qwen2.5-coder:7b` pulled (re-rank step).
7
+ # - The bundle built (`npm -w @esankhan3/code-search-mcp run build`).
8
+ #
9
+ # Optional envs picked up by individual cases:
10
+ # - OPENAI_API_KEY → T5 OpenAI embedding path
11
+ # - GEMINI_OAUTH_CREDS=~/.gemini/oauth_creds.json → T5b Gemini path
12
+ #
13
+ # Exit non-zero on the first failure. The script is idempotent: every case
14
+ # writes under $TMP/ and cleans up its own state at the start.
15
+
16
+ set -u
17
+ cd "$(dirname "$0")/../.." # → packages/code-search-mcp
18
+
19
+ TMP="${TMP:-/tmp/cs-smoke}"
20
+ DIST_CLI="$PWD/dist/cli/index.js"
21
+ DIST_MCP="$PWD/dist/index.js"
22
+ DIST_DAEMON="$PWD/dist/daemon/index.js"
23
+
24
+ if [[ ! -f "$DIST_CLI" || ! -f "$DIST_MCP" || ! -f "$DIST_DAEMON" ]]; then
25
+ echo "FATAL: dist/ missing — run \`npm -w @esankhan3/code-search-mcp run build\` first" >&2
26
+ exit 64
27
+ fi
28
+
29
+ PASS=0
30
+ FAIL=0
31
+ SKIP=0
32
+
33
+ pass() { echo " ✓ $*"; PASS=$((PASS+1)); }
34
+ fail() { echo " ✗ $*" >&2; FAIL=$((FAIL+1)); }
35
+ skip() { echo " - $* (skipped)"; SKIP=$((SKIP+1)); }
36
+ hdr() { echo; echo "── $* ──"; }
37
+
38
+ # ── Setup: minimal pet-shop repo ─────────────────────────────────────────────
39
+ hdr "Setup"
40
+ rm -rf "$TMP"
41
+ mkdir -p "$TMP/pet-shop/src" "$TMP/index"
42
+ cat > "$TMP/pet-shop/src/auth.ts" <<'EOF'
43
+ export function verifyToken(token: string, secret: string): boolean { return token.length > 0 && secret.length > 0; }
44
+ export function requireScope(scope: string, scopes: string[]): void { if (!scopes.includes(scope)) throw new Error('forbidden'); }
45
+ EOF
46
+ cat > "$TMP/pet-shop/src/pets.ts" <<'EOF'
47
+ export interface Pet { id: string; name: string; available: boolean }
48
+ const pets = new Map<string, Pet>();
49
+ export function listAvailablePets(): Pet[] { return [...pets.values()].filter((p) => p.available); }
50
+ export function adoptPet(id: string): Pet { const p = pets.get(id); if (!p) throw new Error('not found'); p.available = false; return p; }
51
+ EOF
52
+ ( cd "$TMP/pet-shop" && git init -q && git config user.email t@t.t && git config user.name t && git add -A && git commit -q -m initial ) || { fail "git init"; exit 1; }
53
+ pass "pet-shop fixture committed"
54
+
55
+ # Common envs for the rest of the smoke.
56
+ export CODE_SEARCH_DATA_DIR="$TMP/index"
57
+ export CODE_SEARCH_EMBEDDING_PROVIDER=ollama
58
+ export CODE_SEARCH_EMBEDDING_MODEL=bge-m3
59
+ export CODE_SEARCH_EMBEDDING_DIMENSIONS=1024
60
+ export CODE_SEARCH_RERANKER_PROVIDER=none
61
+ export CODE_SEARCH_LLM_MODE=none
62
+ export CODE_SEARCH_DAEMON_DISABLED=1
63
+
64
+ # ── T1: --print-config ───────────────────────────────────────────────────────
65
+ hdr "T1: --print-config"
66
+ OUT="$(CODE_SEARCH_EMBEDDING_PROVIDER=ollama node "$DIST_CLI" --print-config --retrieval.max-chunks 12 2>&1)" || { fail "exited non-zero"; }
67
+ echo "$OUT" | grep -q '"provider": "ollama"' && pass "env layer applied" || fail "embedding.provider != ollama"
68
+ echo "$OUT" | grep -q '"maxChunks": 12' && pass "CLI flag applied" || fail "retrieval.maxChunks != 12 (CLI flag override)"
69
+
70
+ # ── T2: code-search index against real Ollama ───────────────────────────────
71
+ hdr "T2: index pet-shop (Ollama bge-m3)"
72
+ OUT="$(node "$DIST_CLI" index "$TMP/pet-shop" --project petshop 2>&1)" || { fail "indexer exited non-zero"; echo "$OUT" >&2; }
73
+ echo "$OUT" | grep -q '"chunks":' && pass "indexer printed chunks summary" || fail "indexer summary missing"
74
+ echo "$OUT" | grep -q 'DEPRECATED: CODE_SEARCH_LLM_MODE' && fail "F1 regression — stale ANVIL_LLM_MODE warning fired" || pass "F1: no stale legacy-env warning"
75
+
76
+ # ── T3: query (vector + bm25 baseline; reranker disabled) ───────────────────
77
+ hdr "T3: code-search query"
78
+ OUT="$(node "$DIST_CLI" query "adopt pet" --project petshop --mode vector --top-k 1 --format json 2>&1)" || { fail "vector query failed"; }
79
+ echo "$OUT" | grep -q '"filePath": "src/pets.ts"' && pass "vector query surfaced pets.ts" || fail "vector query did not surface pets.ts"
80
+ OUT="$(node "$DIST_CLI" query "adoptPet" --project petshop --mode bm25 --top-k 1 --format json 2>&1)" || { fail "bm25 query failed"; }
81
+ echo "$OUT" | grep -q '"filePath": "src/pets.ts"' && pass "bm25 query surfaced pets.ts" || fail "bm25 query did not surface pets.ts"
82
+
83
+ # ── T4: status reports the index ────────────────────────────────────────────
84
+ hdr "T4: status"
85
+ OUT="$(node "$DIST_CLI" status --project petshop 2>&1)"
86
+ echo "$OUT" | grep -q '"embeddingProvider": "ollama"' && pass "embeddingProvider=ollama" || fail "status embeddingProvider mismatch"
87
+ echo "$OUT" | grep -q '"totalChunks":' && pass "totalChunks reported" || fail "totalChunks missing"
88
+
89
+ # ── T5: issue #6 — env vars reach the embedder (provider switch) ────────────
90
+ hdr "T5: env-vars-reach-embedder (issue #6 reproduction)"
91
+ if [[ -n "${OPENAI_API_KEY:-}" ]]; then
92
+ OUT="$(CODE_SEARCH_DATA_DIR="$TMP/index-openai" CODE_SEARCH_EMBEDDING_PROVIDER=openai CODE_SEARCH_EMBEDDING_MODEL=text-embedding-3-small CODE_SEARCH_EMBEDDING_DIMENSIONS=512 CODE_SEARCH_EMBEDDING_API_KEY="$OPENAI_API_KEY" node "$DIST_CLI" index "$TMP/pet-shop" --project petshop-openai 2>&1)"
93
+ # Either embedding succeeds (real key) OR we get OpenAI's auth error
94
+ # (placeholder key). Both prove the env var reached the embedder.
95
+ if echo "$OUT" | grep -qE '("chunks":|api.openai.com|OpenAI embedding request)'; then
96
+ pass "OpenAI embedder constructed + called (env vars threaded through)"
97
+ else
98
+ fail "OpenAI embedder didn't run"; echo "$OUT" | tail -5 >&2
99
+ fi
100
+ else
101
+ skip "T5 OpenAI path — set OPENAI_API_KEY to verify"
102
+ fi
103
+
104
+ # ── T6: vector-space mismatch guard ─────────────────────────────────────────
105
+ hdr "T6: vector-space mismatch guard"
106
+ OUT="$(CODE_SEARCH_EMBEDDING_PROVIDER=openai CODE_SEARCH_EMBEDDING_API_KEY=stub-key node "$DIST_CLI" query "adopt pet" --project petshop --mode vector --top-k 1 --format json 2>&1)" || true
107
+ echo "$OUT" | grep -q 'Vector-space mismatch' && pass "guard refused the cross-provider query" || fail "guard did not fire on provider mismatch"
108
+
109
+ # ── T7: daemon round-trip ───────────────────────────────────────────────────
110
+ hdr "T7: daemon round-trip"
111
+ unset CODE_SEARCH_DAEMON_DISABLED
112
+ DAEMON_LOG="$TMP/daemon.log"
113
+ node "$DIST_DAEMON" --workspace "$TMP/pet-shop" > "$DAEMON_LOG" 2>&1 &
114
+ DAEMON_PID=$!
115
+ sleep 6
116
+ if kill -0 "$DAEMON_PID" 2>/dev/null; then
117
+ pass "daemon process alive (pid $DAEMON_PID)"
118
+ SOCKET="$TMP/index/daemon/pet-shop.sock"
119
+ test -S "$SOCKET" && pass "UDS socket present" || fail "socket missing at $SOCKET"
120
+ OUT="$(node "$DIST_CLI" query "adopt pet" --project pet-shop --mode vector --top-k 1 --format json 2>&1)"
121
+ echo "$OUT" | grep -q '"filePath": "src/pets.ts"' && pass "query through daemon succeeded" || fail "daemon-backed query failed"
122
+ echo "// touched $(date)" >> "$TMP/pet-shop/src/pets.ts"
123
+ sleep 3
124
+ grep -q 'reindex queued' "$DAEMON_LOG" && pass "watcher debounce-reindexed on file touch" || fail "watcher didn't fire"
125
+ kill "$DAEMON_PID" 2>/dev/null
126
+ for _ in 1 2 3 4 5; do
127
+ kill -0 "$DAEMON_PID" 2>/dev/null || break
128
+ sleep 1
129
+ done
130
+ kill -9 "$DAEMON_PID" 2>/dev/null || true
131
+ wait "$DAEMON_PID" 2>/dev/null || true
132
+ else
133
+ fail "daemon exited prematurely"; tail -10 "$DAEMON_LOG" >&2
134
+ fi
135
+ export CODE_SEARCH_DAEMON_DISABLED=1
136
+
137
+ # ── T8: HTTP server endpoints ───────────────────────────────────────────────
138
+ hdr "T8: HTTP serve"
139
+ PORT=$(( ( RANDOM % 1000 ) + 4100 ))
140
+ HTTP_LOG="$TMP/http.log"
141
+ CODE_SEARCH_PORT="$PORT" CODE_SEARCH_HOST=127.0.0.1 CODE_SEARCH_TRANSPORT=streamable-http node "$DIST_MCP" --serve --project petshop > "$HTTP_LOG" 2>&1 &
142
+ HTTP_PID=$!
143
+ sleep 5
144
+ if kill -0 "$HTTP_PID" 2>/dev/null; then
145
+ for ep in health ready version metrics admin/api/status; do
146
+ code="$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:${PORT}/${ep}")"
147
+ if [[ "$code" == "200" ]]; then pass "GET /$ep → 200"; else fail "GET /$ep → $code"; fi
148
+ done
149
+ curl -s "http://127.0.0.1:${PORT}/metrics" | grep -q '^# HELP code_search_queries_total' \
150
+ && pass "/metrics emits Prom text format" || fail "/metrics missing prelude"
151
+ kill "$HTTP_PID" 2>/dev/null
152
+ for _ in 1 2 3 4 5; do
153
+ kill -0 "$HTTP_PID" 2>/dev/null || break
154
+ sleep 1
155
+ done
156
+ kill -9 "$HTTP_PID" 2>/dev/null || true
157
+ wait "$HTTP_PID" 2>/dev/null || true
158
+ else
159
+ fail "HTTP server failed to start"; tail -10 "$HTTP_LOG" >&2
160
+ fi
161
+
162
+ # ── Summary ─────────────────────────────────────────────────────────────────
163
+ echo
164
+ echo "──────────────────────────────────────────"
165
+ echo "PASS: $PASS FAIL: $FAIL SKIP: $SKIP"
166
+ echo "──────────────────────────────────────────"
167
+ exit $(( FAIL == 0 ? 0 : 1 ))