from fam_edge.model_adapters.gemini_adapter import GeminiAdapter def _cfg(**overrides): base = { "provider": "gemini", "model_name": "gemini-flash-latest", "api_key": "key-primary", "circuit_breaker": {"enabled": False}, } base.update(overrides) return base def test_single_key_backward_compat(): a = GeminiAdapter(_cfg()) assert a.api_keys == ["key-primary"] assert a.api_key == "key-primary" def test_extra_keys_appended_in_order(): a = GeminiAdapter(_cfg(extra_api_keys=["key-2", "key-3", "key-4"])) assert a.api_keys == ["key-primary", "key-2", "key-3", "key-4"] def test_extra_keys_dedup_against_primary(): a = GeminiAdapter(_cfg(extra_api_keys=["key-primary", "key-2"])) assert a.api_keys == ["key-primary", "key-2"] def test_missing_env_var_keys_are_dropped(): """extra_api_keys 里未设置的 ${ENV_VAR} 解析为空字符串,不应该混进 api_keys 列表。""" a = GeminiAdapter(_cfg(extra_api_keys=["${SOME_UNSET_GEMINI_KEY_VAR}", "key-2"])) assert a.api_keys == ["key-primary", "key-2"] def test_no_keys_at_all(): a = GeminiAdapter(_cfg(api_key="")) assert a.api_keys == [] assert a.api_key == "" def test_rotated_keys_starts_at_zero_first_call(): a = GeminiAdapter(_cfg(extra_api_keys=["key-2", "key-3", "key-4"])) order = a._rotated_keys() assert [k for _, k in order] == ["key-primary", "key-2", "key-3", "key-4"] assert [i for i, _ in order] == [0, 1, 2, 3] def test_rotated_keys_advances_each_call_evenly(): """核心诉求: 连续调用应该轮流从不同 key 起手,而不是每次都从 0 号开始 (旧实现的 bug:0 号 key 的 fallback 模型通常最终能成功,导致其余 key 常年闲置)。""" a = GeminiAdapter(_cfg(extra_api_keys=["key-2", "key-3", "key-4"])) starts = [a._rotated_keys()[0][1] for _ in range(4)] assert starts == ["key-primary", "key-2", "key-3", "key-4"] # 转满一圈后应该回到起点 assert a._rotated_keys()[0][1] == "key-primary" def test_rotated_keys_wraps_around_correctly(): a = GeminiAdapter(_cfg(extra_api_keys=["key-2", "key-3", "key-4"])) a._key_rotation_idx = 3 # 手动模拟"上次从 4 号 key 起手" order = a._rotated_keys() assert [k for _, k in order] == ["key-4", "key-primary", "key-2", "key-3"] def test_rotated_keys_single_key_never_errors(): a = GeminiAdapter(_cfg()) for _ in range(3): assert a._rotated_keys() == [(0, "key-primary")]