11#! /usr/bin/env bats
2+ # shellcheck disable=SC2030,SC2031
23bats_require_minimum_version 1.5.0
34
45setup () {
@@ -15,16 +16,17 @@ setup() {
1516 run-prometheus.sh \
1617 run-tempo.sh \
1718 run-pyroscope.sh; do
18- cat > " $TESTDIR /$script " << 'EOF '
19+ cat > " $TESTDIR /$script " << 'SCRIPT '
1920#!/usr/bin/env bash
2021sleep 60
21- EOF
22+ SCRIPT
2223 chmod +x " $TESTDIR /$script "
2324 done
2425
25- cat > " $TESTDIR /curl" << 'EOF '
26+ cat > " $TESTDIR /curl" << 'SCRIPT '
2627#!/usr/bin/env bash
2728args="$*"
29+ mode="${STUB_SA_MODE:-success}"
2830
2931if [[ "$args" == *"/ready"* ||
3032 "$args" == *"/api/health"* ||
@@ -33,23 +35,45 @@ if [[ "$args" == *"/ready"* ||
3335 exit 0
3436fi
3537
38+ if [[ "$args" == *"/api/serviceaccounts/1/tokens"* && "$args" == *"-X DELETE"* ]]; then
39+ printf '{}'
40+ exit 0
41+ fi
42+
3643if [[ "$args" == *"/api/serviceaccounts/1/tokens"* && "$args" == *"-d"* ]]; then
37- printf '{"key":"token123"}'
44+ if [[ "$mode" == "success" || "$mode" == "with_existing_token" ]]; then
45+ printf '{"key":"token123"}'
46+ fi
3847 exit 0
3948fi
4049
4150if [[ "$args" == *"/api/serviceaccounts/1/tokens"* ]]; then
42- printf '[]'
51+ if [[ "$mode" == "with_existing_token" ]]; then
52+ printf '[{"id":99,"name":"ai-tools-token"}]'
53+ else
54+ printf '[]'
55+ fi
56+ exit 0
57+ fi
58+
59+ if [[ "$args" == *"/api/serviceaccounts/search?query=ai-tools"* ]]; then
60+ if [[ "$mode" == "lookup_existing" ]]; then
61+ printf '{"serviceAccounts":[{"id":1,"name":"ai-tools"}]}'
62+ else
63+ printf '{}'
64+ fi
4365 exit 0
4466fi
4567
4668if [[ "$args" == *"/api/serviceaccounts"* && "$args" == *"-d"* ]]; then
47- printf '{"id":1}'
69+ if [[ "$mode" == "success" || "$mode" == "with_existing_token" ]]; then
70+ printf '{"id":1}'
71+ fi
4872 exit 0
4973fi
5074
5175printf '{}'
52- EOF
76+ SCRIPT
5377 chmod +x " $TESTDIR /curl"
5478}
5579
@@ -58,7 +82,7 @@ teardown() {
5882}
5983
6084run_run_all () {
61- local version=$1
85+ local version=${1 :- latest}
6286 cd " $TESTDIR " || return 1
6387 PATH=" $TESTDIR :$PATH " \
6488 LGTM_CONFIG_DIR=" $CONFIGDIR " \
@@ -68,78 +92,141 @@ run_run_all() {
6892 timeout 3s bash ./run-all.sh
6993}
7094
95+ run_mcp_case () {
96+ local tempo_enabled=$1
97+ local sa_mode=$2
98+ local version=${3:- latest}
99+ if [[ " $tempo_enabled " == " true" ]]; then
100+ export TEMPO_EXTRA_ARGS=" --query-frontend.mcp-server.enabled=true"
101+ else
102+ unset TEMPO_EXTRA_ARGS
103+ fi
104+ export STUB_SA_MODE=" $sa_mode "
105+ run run_run_all " $version "
106+ }
107+
108+ assert_contains () {
109+ local needle=$1
110+ [[ " $output " == * " $needle " * ]]
111+ }
112+
113+ assert_not_contains () {
114+ local needle=$1
115+ [[ " $output " != * " $needle " * ]]
116+ }
117+
118+ assert_has_file () {
119+ [ -f " $1 " ]
120+ }
121+
122+ assert_no_file () {
123+ [ ! -f " $1 " ]
124+ }
125+
126+ assert_file_contains () {
127+ grep -Fq " $2 " " $1 "
128+ }
129+
130+ assert_file_not_contains () {
131+ ! grep -Fq " $2 " " $1 "
132+ }
133+
71134@test " docs URL uses main for latest" {
72135 local expected=" https://github.com/grafana/docker-otel-lgtm/blob/main/docs/mcp-integration.md"
73- local expected_line=" Docs: $expected "
74- run run_run_all " latest"
75- [[ " $output " == * " $expected_line " * ]]
76- [[ " $output " != * " /blob/vlatest/" * ]]
136+ run run_run_all latest
137+ assert_contains " Docs: $expected "
138+ assert_not_contains " /blob/vlatest/"
77139}
78140
79141@test " docs URL uses main when version is empty" {
80142 local expected=" https://github.com/grafana/docker-otel-lgtm/blob/main/docs/mcp-integration.md"
81- local expected_line=" Docs: $expected "
82143 run run_run_all " "
83- [[ " $output " == * " $expected_line " * ]]
144+ assert_contains " Docs: $expected "
84145}
85146
86-
87147@test " docs URL uses main for main tag" {
88148 local expected=" https://github.com/grafana/docker-otel-lgtm/blob/main/docs/mcp-integration.md"
89- local expected_line=" Docs: $expected "
90- run run_run_all " main"
91- [[ " $output " == * " $expected_line " * ]]
92- [[ " $output " != * " /blob/vmain/" * ]]
149+ run run_run_all main
150+ assert_contains " Docs: $expected "
151+ assert_not_contains " /blob/vmain/"
93152}
94153
95154@test " printed MCP commands escape configurable paths" {
96155 local configdir=" $TESTDIR /etc/lgtm with spaces"
97156 local escaped_configdir=${configdir// / \\ }
98- CONFIGDIR=" $configdir " run run_run_all " latest"
99- [[ " $output " == * " bash <(docker exec lgtm cat ${escaped_configdir} /claude-mcp-setup.sh)" * ]]
100- [[ " $output " == * " docker exec lgtm cat ${escaped_configdir} /mcp.json" * ]]
157+ CONFIGDIR=" $configdir " run run_run_all latest
158+ assert_contains " bash <(docker exec lgtm cat ${escaped_configdir} /claude-mcp-setup.sh)"
159+ assert_contains " docker exec lgtm cat ${escaped_configdir} /mcp.json"
101160}
102161
103162@test " docs URL prefixes bare release version with v" {
104163 local expected
105164 expected=" https://github.com/grafana/docker-otel-lgtm/blob/v1.2.3-test/docs/mcp-integration.md"
106- local expected_line=" Docs: $expected "
107- run run_run_all " 1.2.3-test"
108- [[ " $output " == * " $expected_line " * ]]
165+ run run_run_all 1.2.3-test
166+ assert_contains " Docs: $expected "
109167}
110168
111169@test " docs URL does not double-prefix version that already starts with v" {
112170 local expected
113171 expected=" https://github.com/grafana/docker-otel-lgtm/blob/v1.2.3-test/docs/mcp-integration.md"
114- local expected_line=" Docs: $expected "
115- run run_run_all " v1.2.3-test"
116- [[ " $output " == * " $expected_line " * ]]
117- [[ " $output " != * " /blob/vv1.2.3-test/" * ]]
118- }
119-
120- @test " MCP bootstrap writes helper artifacts with expected contents" {
121- run run_run_all " latest"
122- [ -f " $CONFIGDIR /mcp.json" ]
123- [ -f " $CONFIGDIR /claude-mcp-setup.sh" ]
124- [ -f " $TOKENFILE " ]
125-
126- grep -Fq \
127- ' "GRAFANA_URL": "http://localhost:3000"' \
128- " $CONFIGDIR /mcp.json"
129- grep -Fq \
130- ' "GRAFANA_SERVICE_ACCOUNT_TOKEN": "token123"' \
131- " $CONFIGDIR /mcp.json"
132- grep -Fq ' "url": "http://localhost:3200/api/mcp"' " $CONFIGDIR /mcp.json"
133-
134- grep -Fq \
135- ' claude mcp add grafana -e "GRAFANA_URL=http://localhost:3000"' \
136- " $CONFIGDIR /claude-mcp-setup.sh"
137- grep -Fq \
138- ' GRAFANA_SERVICE_ACCOUNT_TOKEN=token123' \
139- " $CONFIGDIR /claude-mcp-setup.sh"
140- grep -Fq \
141- ' claude mcp add --transport http tempo "http://localhost:3200/api/mcp"' \
142- " $CONFIGDIR /claude-mcp-setup.sh"
143-
144- grep -Fqx ' token123' " $TOKENFILE "
172+ run run_run_all v1.2.3-test
173+ assert_contains " Docs: $expected "
174+ assert_not_contains " /blob/vv1.2.3-test/"
175+ }
176+
177+ @test " tempo enabled with service account writes both MCP servers" {
178+ run_mcp_case true success latest
179+ assert_contains " Tempo MCP: server enabled at http://localhost:3200/api/mcp"
180+ assert_contains " Grafana MCP: server enabled with service account token"
181+ assert_contains " - 3200: Tempo endpoint (MCP at http://localhost:3200/api/mcp)"
182+ assert_has_file " $CONFIGDIR /mcp.json"
183+ assert_has_file " $CONFIGDIR /claude-mcp-setup.sh"
184+ assert_has_file " $TOKENFILE "
185+ assert_file_contains " $CONFIGDIR /mcp.json" ' "grafana"'
186+ assert_file_contains " $CONFIGDIR /mcp.json" ' "tempo"'
187+ assert_file_contains " $CONFIGDIR /mcp.json" ' GRAFANA_SERVICE_ACCOUNT_TOKEN": "token123"'
188+ assert_file_contains " $CONFIGDIR /claude-mcp-setup.sh" ' claude mcp add grafana'
189+ assert_file_contains " $CONFIGDIR /claude-mcp-setup.sh" ' claude mcp add --transport http tempo'
190+ assert_file_contains " $TOKENFILE " ' token123'
191+ }
192+
193+ @test " tempo disabled with service account writes grafana-only MCP config" {
194+ run_mcp_case false success latest
195+ assert_contains " Tempo MCP: server disabled; enable with"
196+ assert_contains " TEMPO_EXTRA_ARGS=--query-frontend.mcp-server.enabled=true"
197+ assert_contains " Grafana MCP: server enabled with service account token"
198+ assert_contains " - 3200: Tempo endpoint"
199+ assert_not_contains " - 3200: Tempo endpoint (MCP at http://localhost:3200/api/mcp)"
200+ assert_has_file " $CONFIGDIR /mcp.json"
201+ assert_has_file " $CONFIGDIR /claude-mcp-setup.sh"
202+ assert_has_file " $TOKENFILE "
203+ assert_file_contains " $CONFIGDIR /mcp.json" ' "grafana"'
204+ assert_file_not_contains " $CONFIGDIR /mcp.json" ' "tempo"'
205+ assert_file_contains " $CONFIGDIR /claude-mcp-setup.sh" ' claude mcp add grafana'
206+ assert_file_not_contains " $CONFIGDIR /claude-mcp-setup.sh" ' claude mcp add --transport http tempo'
207+ }
208+
209+ @test " tempo enabled without service account writes tempo-only MCP config" {
210+ run_mcp_case true missing latest
211+ assert_contains " Tempo MCP: server enabled at http://localhost:3200/api/mcp"
212+ assert_contains " Grafana MCP: server unavailable; could not create service account token"
213+ assert_has_file " $CONFIGDIR /mcp.json"
214+ assert_has_file " $CONFIGDIR /claude-mcp-setup.sh"
215+ assert_no_file " $TOKENFILE "
216+ assert_file_not_contains " $CONFIGDIR /mcp.json" ' "grafana"'
217+ assert_file_contains " $CONFIGDIR /mcp.json" ' "tempo"'
218+ assert_file_not_contains " $CONFIGDIR /claude-mcp-setup.sh" ' claude mcp add grafana'
219+ assert_file_contains " $CONFIGDIR /claude-mcp-setup.sh" ' claude mcp add --transport http tempo'
220+ }
221+
222+ @test " tempo disabled without service account writes empty MCP config" {
223+ run_mcp_case false missing latest
224+ assert_contains " Tempo MCP: server disabled; enable with"
225+ assert_contains " TEMPO_EXTRA_ARGS=--query-frontend.mcp-server.enabled=true"
226+ assert_contains " Grafana MCP: server unavailable; could not create service account token"
227+ assert_has_file " $CONFIGDIR /mcp.json"
228+ assert_has_file " $CONFIGDIR /claude-mcp-setup.sh"
229+ assert_no_file " $TOKENFILE "
230+ assert_file_contains " $CONFIGDIR /mcp.json" ' "mcpServers": {}'
231+ assert_file_not_contains " $CONFIGDIR /claude-mcp-setup.sh" ' claude mcp add '
145232}
0 commit comments