From f8055094bce4354df40ed38811381033223ccc0c Mon Sep 17 00:00:00 2001 From: chelsealong Date: Tue, 15 Sep 2026 20:02:07 +0000 Subject: [PATCH] fix(cli): raise a clear error when conformance replay recordings are missing adk conformance test crashed with AttributeError: 'NoneType' object has no attribute 'recordings' when the ADK web server it talks to over HTTP wasn't started with ReplayPlugin loaded (e.g. only RecordingsPlugin was passed via --extra_plugins, which is what record mode needs but replay does not use). _ConformanceTestGemini now raises a ReplayVerificationError that names the missing plugin and the --extra_plugins flag needed to fix it, instead of crashing on an unrelated attribute access. Fixes #7128 --- .../_conformance_test_google_llm.py | 7 +++ .../test_conformance_test_google_llm.py | 53 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/unittests/cli/conformance/test_conformance_test_google_llm.py diff --git a/src/google/adk/cli/conformance/_conformance_test_google_llm.py b/src/google/adk/cli/conformance/_conformance_test_google_llm.py index b1b86e06e0c..374c0330e35 100644 --- a/src/google/adk/cli/conformance/_conformance_test_google_llm.py +++ b/src/google/adk/cli/conformance/_conformance_test_google_llm.py @@ -239,6 +239,13 @@ def __init__( ) -> None: super().__init__(**kwargs) recordings = config.get('_adk_replay_recordings') + if recordings is None: + raise ReplayVerificationError( + 'Replay recordings were not loaded. The ADK web server must be' + ' started with the replay plugin, e.g. `adk web' + ' --extra_plugins=google.adk.cli.plugins.replay_plugin.ReplayPlugin`,' + ' for `adk conformance test` to work.' + ) self._user_message_index = config.get('user_message_index') self._agent_name = config.get('agent_name') self._replay_index = config.get('current_replay_index') diff --git a/tests/unittests/cli/conformance/test_conformance_test_google_llm.py b/tests/unittests/cli/conformance/test_conformance_test_google_llm.py new file mode 100644 index 00000000000..a9c174e31c5 --- /dev/null +++ b/tests/unittests/cli/conformance/test_conformance_test_google_llm.py @@ -0,0 +1,53 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for _ConformanceTestGemini's handling of missing replay recordings.""" + +from google.adk.cli.conformance._conformance_test_google_llm import _ConformanceTestGemini +from google.adk.cli.conformance._conformance_test_google_llm import ReplayVerificationError +from google.adk.cli.plugins.recordings_schema import Recordings +import pytest + + +def test_missing_recordings_raises_actionable_error(): + """Replaying without the replay plugin loaded should fail clearly. + + `config['_adk_replay_recordings']` is only populated by ReplayPlugin's + before_run_callback. If the server serving `adk conformance test` wasn't + started with that plugin, the key is absent and `config.get(...)` returns + None. Silently indexing into `None.recordings` used to raise a bare + AttributeError; it should now explain what's missing. + """ + config = { + 'user_message_index': 0, + 'agent_name': 'root_agent', + 'current_replay_index': 0, + } + + with pytest.raises(ReplayVerificationError, match='ReplayPlugin'): + _ConformanceTestGemini(config=config) + + +def test_present_recordings_does_not_raise(): + """Sanity check: a properly loaded config constructs without error.""" + config = { + '_adk_replay_recordings': Recordings(recordings=[]), + 'user_message_index': 0, + 'agent_name': 'root_agent', + 'current_replay_index': 0, + } + + model = _ConformanceTestGemini(config=config) + + assert model._agent_llm_recordings == []