Skip to content

Commit b1e3fbf

Browse files
CopilotBlueion76
andauthored
Fix load_secret() to fall through to env var when Docker secret file is empty
Agent-Logs-Url: https://github.com/Blueion76/OctoGen/sessions/f0c3a110-5fd0-43b6-9caa-d0117413cc1b Co-authored-by: Blueion76 <128919662+Blueion76@users.noreply.github.com>
1 parent c75955a commit b1e3fbf

2 files changed

Lines changed: 86 additions & 1 deletion

File tree

octogen/utils/secrets.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ def load_secret(secret_name: str, default: Optional[str] = None) -> Optional[str
2424
secret_path = Path(f"/run/secrets/{secret_name.lower()}")
2525
if secret_path.exists():
2626
try:
27-
return secret_path.read_text().strip()
27+
value = secret_path.read_text().strip()
28+
if value:
29+
return value
2830
except Exception:
2931
pass
3032

tests/test_secrets.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""Tests for Docker secrets / environment variable loading in load_secret()."""
2+
3+
import os
4+
from pathlib import Path
5+
from unittest.mock import patch
6+
7+
import pytest
8+
9+
from octogen.utils.secrets import load_secret
10+
11+
12+
class TestLoadSecret:
13+
"""Tests for load_secret() priority and fallback behaviour."""
14+
15+
def test_env_var_returned_when_no_docker_secret_file(self):
16+
"""Env var is returned when the Docker secrets file does not exist."""
17+
with patch.dict(os.environ, {"SPOTIFY_CLIENT_ID": "env_value"}, clear=True):
18+
with patch("octogen.utils.secrets.Path") as mock_path_cls:
19+
mock_path_cls.return_value.exists.return_value = False
20+
result = load_secret("SPOTIFY_CLIENT_ID", "")
21+
assert result == "env_value"
22+
23+
def test_docker_secret_returned_when_non_empty(self, tmp_path):
24+
"""A non-empty Docker secrets file is returned in preference to the env var."""
25+
secret_file = tmp_path / "spotify_client_id"
26+
secret_file.write_text("docker_secret_value\n")
27+
28+
with patch.dict(os.environ, {"SPOTIFY_CLIENT_ID": "env_value"}, clear=True):
29+
with patch("octogen.utils.secrets.Path") as mock_path_cls:
30+
mock_path_cls.return_value.exists.return_value = True
31+
mock_path_cls.return_value.read_text.return_value = "docker_secret_value\n"
32+
result = load_secret("SPOTIFY_CLIENT_ID", "")
33+
assert result == "docker_secret_value"
34+
35+
def test_empty_docker_secret_falls_through_to_env_var(self):
36+
"""An empty Docker secrets file must NOT shadow a non-empty env var.
37+
38+
This is the core regression: previously load_secret() returned '' from
39+
an empty secrets file without ever reading the environment variable,
40+
causing SPOTIFY_IMPORT_ENABLED=true to be ineffective even when all
41+
required env vars were correctly set.
42+
"""
43+
with patch.dict(os.environ, {"SPOTIFY_CLIENT_ID": "real_client_id"}, clear=True):
44+
with patch("octogen.utils.secrets.Path") as mock_path_cls:
45+
mock_path_cls.return_value.exists.return_value = True
46+
# Simulates a Docker secrets file that exists but is empty
47+
mock_path_cls.return_value.read_text.return_value = ""
48+
result = load_secret("SPOTIFY_CLIENT_ID", "")
49+
assert result == "real_client_id"
50+
51+
def test_whitespace_only_docker_secret_falls_through_to_env_var(self):
52+
"""A whitespace-only secrets file is treated as empty and falls through."""
53+
with patch.dict(os.environ, {"SPOTIFY_CLIENT_ID": "real_client_id"}, clear=True):
54+
with patch("octogen.utils.secrets.Path") as mock_path_cls:
55+
mock_path_cls.return_value.exists.return_value = True
56+
mock_path_cls.return_value.read_text.return_value = " \n "
57+
result = load_secret("SPOTIFY_CLIENT_ID", "")
58+
assert result == "real_client_id"
59+
60+
def test_returns_default_when_no_secret_and_no_env_var(self):
61+
"""Default is returned when neither Docker secret nor env var provides a value."""
62+
with patch.dict(os.environ, {}, clear=True):
63+
with patch("octogen.utils.secrets.Path") as mock_path_cls:
64+
mock_path_cls.return_value.exists.return_value = False
65+
result = load_secret("SPOTIFY_CLIENT_ID", "fallback")
66+
assert result == "fallback"
67+
68+
def test_returns_none_default_when_nothing_set(self):
69+
"""None (the default default) is returned when nothing is configured."""
70+
with patch.dict(os.environ, {}, clear=True):
71+
with patch("octogen.utils.secrets.Path") as mock_path_cls:
72+
mock_path_cls.return_value.exists.return_value = False
73+
result = load_secret("SPOTIFY_CLIENT_ID")
74+
assert result is None
75+
76+
def test_docker_secret_read_error_falls_through_to_env_var(self):
77+
"""If reading the secrets file raises an exception, the env var is used."""
78+
with patch.dict(os.environ, {"SPOTIFY_CLIENT_ID": "env_fallback"}, clear=True):
79+
with patch("octogen.utils.secrets.Path") as mock_path_cls:
80+
mock_path_cls.return_value.exists.return_value = True
81+
mock_path_cls.return_value.read_text.side_effect = OSError("permission denied")
82+
result = load_secret("SPOTIFY_CLIENT_ID", "")
83+
assert result == "env_fallback"

0 commit comments

Comments
 (0)