@@ -38,16 +38,26 @@ def test_resolve_api_key_from_instance(self):
3838 assert s .resolve_api_key () == "sk-test-123"
3939
4040 def test_resolve_api_key_from_env (self , monkeypatch ):
41+ monkeypatch .delenv ("OPENHARNESS_ANTHROPIC_API_KEY" , raising = False )
4142 monkeypatch .setenv ("ANTHROPIC_API_KEY" , "sk-env-456" )
4243 s = Settings ()
4344 assert s .resolve_api_key () == "sk-env-456"
4445
46+ def test_resolve_api_key_prefers_openharness_env (self , monkeypatch ):
47+ monkeypatch .setenv ("OPENHARNESS_ANTHROPIC_API_KEY" , "sk-oh-456" )
48+ monkeypatch .setenv ("ANTHROPIC_API_KEY" , "sk-env-456" )
49+ s = Settings ()
50+ assert s .resolve_api_key () == "sk-oh-456"
51+
4552 def test_resolve_api_key_instance_takes_precedence (self , monkeypatch ):
53+ monkeypatch .delenv ("OPENHARNESS_ANTHROPIC_API_KEY" , raising = False )
4654 monkeypatch .setenv ("ANTHROPIC_API_KEY" , "sk-env-456" )
4755 s = Settings (api_key = "sk-instance-789" )
4856 assert s .resolve_api_key () == "sk-instance-789"
4957
5058 def test_resolve_api_key_missing_raises (self , monkeypatch ):
59+ monkeypatch .delenv ("OPENHARNESS_ANTHROPIC_API_KEY" , raising = False )
60+ monkeypatch .delenv ("OPENHARNESS_OPENAI_API_KEY" , raising = False )
5161 monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
5262 monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
5363 s = Settings ()
@@ -78,6 +88,7 @@ def test_resolve_auth_prefers_env_over_flat_api_key_for_openai(self, monkeypatch
7888 """When api_format=openai, resolve_auth() should use OPENAI_API_KEY
7989 from the environment rather than the flat api_key field which may
8090 contain an Anthropic key from settings.json."""
91+ monkeypatch .delenv ("OPENHARNESS_OPENAI_API_KEY" , raising = False )
8192 monkeypatch .setenv ("OPENAI_API_KEY" , "sk-openai-correct" )
8293 monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
8394 s = Settings (api_key = "sk-ant-wrong-provider" , api_format = "openai" )
@@ -86,9 +97,21 @@ def test_resolve_auth_prefers_env_over_flat_api_key_for_openai(self, monkeypatch
8697 assert auth .value == "sk-openai-correct"
8798 assert "OPENAI" in auth .source
8899
100+ def test_resolve_auth_prefers_openharness_env_for_openai (self , monkeypatch ):
101+ monkeypatch .setenv ("OPENHARNESS_OPENAI_API_KEY" , "sk-oh-openai" )
102+ monkeypatch .setenv ("OPENAI_API_KEY" , "sk-openai-correct" )
103+ monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
104+ s = Settings (api_key = "sk-ant-wrong-provider" , api_format = "openai" )
105+ s = s .sync_active_profile_from_flat_fields ()
106+ auth = s .resolve_auth ()
107+ assert auth .value == "sk-oh-openai"
108+ assert auth .source == "env:OPENHARNESS_OPENAI_API_KEY"
109+
89110 def test_resolve_auth_falls_back_to_flat_api_key (self , monkeypatch ):
90111 """When no provider-specific env var is set, resolve_auth() should
91112 still fall back to the flat api_key field."""
113+ monkeypatch .delenv ("OPENHARNESS_ANTHROPIC_API_KEY" , raising = False )
114+ monkeypatch .delenv ("OPENHARNESS_OPENAI_API_KEY" , raising = False )
92115 monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
93116 monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
94117 s = Settings (api_key = "sk-fallback-key" )
@@ -109,6 +132,32 @@ def test_env_overrides_picks_up_openai_base_url(self, tmp_path: Path, monkeypatc
109132 s = load_settings (path )
110133 assert s .base_url == "https://relay.example.com/v1"
111134
135+ def test_load_settings_uses_profile_specific_openharness_env_key (self , tmp_path : Path , monkeypatch ):
136+ monkeypatch .setenv ("ANTHROPIC_API_KEY" , "sk-ant-wrong" )
137+ monkeypatch .setenv ("OPENHARNESS_OPENAI_API_KEY" , "sk-oh-openai" )
138+ monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
139+ path = tmp_path / "settings.json"
140+ path .write_text (
141+ Settings (active_profile = "openai-compatible" ).model_dump_json (),
142+ encoding = "utf-8" ,
143+ )
144+ s = load_settings (path )
145+ assert s .active_profile == "openai-compatible"
146+ assert s .api_key == "sk-oh-openai"
147+
148+ def test_load_settings_ignores_wrong_provider_native_env_key (self , tmp_path : Path , monkeypatch ):
149+ monkeypatch .setenv ("ANTHROPIC_API_KEY" , "sk-ant-wrong" )
150+ monkeypatch .delenv ("OPENHARNESS_OPENAI_API_KEY" , raising = False )
151+ monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
152+ path = tmp_path / "settings.json"
153+ path .write_text (
154+ Settings (active_profile = "openai-compatible" ).model_dump_json (),
155+ encoding = "utf-8" ,
156+ )
157+ s = load_settings (path )
158+ assert s .active_profile == "openai-compatible"
159+ assert s .api_key == ""
160+
112161 def test_env_overrides_pick_up_compact_threshold_settings (self , tmp_path : Path , monkeypatch ):
113162 monkeypatch .setenv ("OPENHARNESS_CONTEXT_WINDOW_TOKENS" , "123456" )
114163 monkeypatch .setenv ("OPENHARNESS_AUTO_COMPACT_THRESHOLD_TOKENS" , "120000" )
@@ -131,6 +180,8 @@ def test_anthropic_base_url_takes_precedence_over_openai(self, tmp_path: Path, m
131180
132181class TestLoadSaveSettings :
133182 def test_load_missing_file_returns_defaults (self , tmp_path : Path , monkeypatch ):
183+ monkeypatch .delenv ("OPENHARNESS_ANTHROPIC_API_KEY" , raising = False )
184+ monkeypatch .delenv ("OPENHARNESS_OPENAI_API_KEY" , raising = False )
134185 monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
135186 monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
136187 monkeypatch .delenv ("ANTHROPIC_BASE_URL" , raising = False )
@@ -143,6 +194,8 @@ def test_load_missing_file_returns_defaults(self, tmp_path: Path, monkeypatch):
143194 assert s == Settings ().materialize_active_profile ()
144195
145196 def test_load_existing_file (self , tmp_path : Path , monkeypatch ):
197+ monkeypatch .delenv ("OPENHARNESS_ANTHROPIC_API_KEY" , raising = False )
198+ monkeypatch .delenv ("OPENHARNESS_OPENAI_API_KEY" , raising = False )
146199 monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
147200 monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
148201 monkeypatch .delenv ("ANTHROPIC_BASE_URL" , raising = False )
@@ -158,6 +211,8 @@ def test_load_existing_file(self, tmp_path: Path, monkeypatch):
158211 assert s .api_key == "" # default preserved
159212
160213 def test_save_and_load_roundtrip (self , tmp_path : Path , monkeypatch ):
214+ monkeypatch .delenv ("OPENHARNESS_ANTHROPIC_API_KEY" , raising = False )
215+ monkeypatch .delenv ("OPENHARNESS_OPENAI_API_KEY" , raising = False )
161216 monkeypatch .delenv ("ANTHROPIC_API_KEY" , raising = False )
162217 monkeypatch .delenv ("OPENAI_API_KEY" , raising = False )
163218 monkeypatch .delenv ("ANTHROPIC_BASE_URL" , raising = False )
0 commit comments