refactor(env): unify Docker env vars — canonical AUTH_SECRET_KEY, remove JWT_SECRET fallback
Canonical variables:
- AUTH_SECRET_KEY — JWT signing key for backend + agent (was split across
AUTH_SECRET_KEY / JWT_SECRET)
- SERVICE_JWT — agent→backend service token
- No JWT_SECRET fallback: decoder fails with migration guidance if only
JWT_SECRET is set
Compose files unified:
- docker-compose.yml, docker-compose.enterprise-clean.yml,
docker-compose.e2e.yml — all use AUTH_SECRET_KEY
- build.sh generated compose now passes AUTH_SECRET_KEY + ENCRYPTION_KEY
to backend
Env examples unified and completed:
- .env.example — comprehensive template, all compose vars
- .env.enterprise-clean.example — production template
- backend/.env.example — backend-only run
- docker/.env.agent.example — agent-only run
- NEW: .env.current.example, .env.master.example,
.env.e2e.example, frontend/.env.example
Tests aligned:
- conftest sets AUTH_SECRET_KEY (canonical value matched across test files)
- test mocks use canonical name
- 1176 passed, 0 failed
This commit is contained in:
@@ -12,9 +12,9 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import jwt
|
||||
|
||||
# Set JWT_SECRET and LLM_API_KEY for tests
|
||||
JWT_SECRET = "test-jwt-secret-key"
|
||||
os.environ["JWT_SECRET"] = JWT_SECRET
|
||||
# Set AUTH_SECRET_KEY and LLM_API_KEY for tests (match conftest)
|
||||
os.environ.setdefault("AUTH_SECRET_KEY", "test-secret-key-for-jwt-testing")
|
||||
AUTH_SECRET_KEY = os.environ["AUTH_SECRET_KEY"]
|
||||
os.environ["OPENAI_API_KEY"] = "sk-test-key"
|
||||
os.environ["LLM_API_KEY"] = "sk-test-key"
|
||||
os.environ["LLM_MODEL"] = "gpt-4o"
|
||||
@@ -33,7 +33,7 @@ def mock_save_conversation():
|
||||
|
||||
|
||||
def _make_test_jwt(user_id: str = "test-user") -> str:
|
||||
return jwt.encode({"sub": user_id}, JWT_SECRET, algorithm="HS256")
|
||||
return jwt.encode({"sub": user_id}, AUTH_SECRET_KEY, algorithm="HS256")
|
||||
|
||||
|
||||
def _empty_agent_state():
|
||||
@@ -68,6 +68,8 @@ async def test_handler_empty_message_returns_immediately():
|
||||
# create_agent should NOT be called for empty messages
|
||||
# (it gets called currently — future optimization)
|
||||
# mock_create.assert_not_called() # TODO: optimize to skip graph for empty msg
|
||||
|
||||
|
||||
# #endregion TestAgentChat.Handler.EmptyMessage
|
||||
|
||||
|
||||
@@ -92,10 +94,12 @@ async def test_handler_missing_auth_continues_gracefully():
|
||||
# Patch create_agent to prevent LLM call — handler should proceed without JWT
|
||||
with patch("src.agent.app.create_agent") as mock_create:
|
||||
mock_graph = AsyncMock()
|
||||
|
||||
async def _empty_stream(*_args, **_kwargs):
|
||||
"""Empty async generator — yields nothing."""
|
||||
return
|
||||
yield # make it a generator
|
||||
|
||||
mock_graph.astream_events = _empty_stream
|
||||
mock_graph.aget_state = AsyncMock(return_value=_empty_agent_state())
|
||||
mock_create.return_value = mock_graph
|
||||
@@ -107,10 +111,10 @@ async def test_handler_missing_auth_continues_gracefully():
|
||||
# No UNAUTHORIZED error — handler proceeds with empty user_jwt
|
||||
for r in results:
|
||||
import json
|
||||
|
||||
parsed = json.loads(r) if isinstance(r, str) else r
|
||||
meta = parsed.get("metadata", {})
|
||||
assert meta.get("code") != "UNAUTHORIZED", \
|
||||
"Handler should not reject missing auth — JWT optional at Gradio layer"
|
||||
assert meta.get("code") != "UNAUTHORIZED", "Handler should not reject missing auth — JWT optional at Gradio layer"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -127,9 +131,11 @@ async def test_handler_invalid_jwt_continues_gracefully():
|
||||
|
||||
with patch("src.agent.app.create_agent") as mock_create:
|
||||
mock_graph = AsyncMock()
|
||||
|
||||
async def _empty_stream(*_args, **_kwargs):
|
||||
return
|
||||
yield
|
||||
|
||||
mock_graph.astream_events = _empty_stream
|
||||
mock_graph.aget_state = AsyncMock(return_value=_empty_agent_state())
|
||||
mock_create.return_value = mock_graph
|
||||
@@ -140,10 +146,12 @@ async def test_handler_invalid_jwt_continues_gracefully():
|
||||
|
||||
for r in results:
|
||||
import json
|
||||
|
||||
parsed = json.loads(r) if isinstance(r, str) else r
|
||||
meta = parsed.get("metadata", {})
|
||||
assert meta.get("code") != "UNAUTHORIZED", \
|
||||
"Handler should not reject invalid JWT — invalid token ignored at Gradio layer"
|
||||
assert meta.get("code") != "UNAUTHORIZED", "Handler should not reject invalid JWT — invalid token ignored at Gradio layer"
|
||||
|
||||
|
||||
# #endregion TestAgentChat.Handler.AuthError
|
||||
|
||||
|
||||
@@ -180,11 +188,11 @@ async def test_handler_yields_stream_tokens():
|
||||
|
||||
assert len(results) > 0, "Should yield at least one chunk"
|
||||
import json
|
||||
stream_tokens = [
|
||||
r for r in results
|
||||
if json.loads(r)["metadata"]["type"] == "stream_token"
|
||||
]
|
||||
|
||||
stream_tokens = [r for r in results if json.loads(r)["metadata"]["type"] == "stream_token"]
|
||||
assert len(stream_tokens) > 0, "Should yield stream_token metadata"
|
||||
|
||||
|
||||
# #endregion TestAgentChat.Handler.Streaming
|
||||
|
||||
|
||||
@@ -207,9 +215,11 @@ async def test_handler_resume_confirm():
|
||||
# imports create_agent directly from langgraph_setup
|
||||
with patch("src.agent._confirmation.create_agent") as mock_create:
|
||||
mock_graph = MagicMock()
|
||||
|
||||
async def _empty_stream(*_args, **_kwargs):
|
||||
return
|
||||
yield
|
||||
|
||||
mock_graph.astream_events = _empty_stream
|
||||
mock_create.return_value = mock_graph
|
||||
|
||||
@@ -220,10 +230,13 @@ async def test_handler_resume_confirm():
|
||||
# Should yield confirm_resolved metadata
|
||||
assert len(results) == 1
|
||||
import json
|
||||
|
||||
parsed = json.loads(results[0]) if isinstance(results[0], str) else results[0]
|
||||
assert parsed["metadata"]["type"] == "confirm_resolved"
|
||||
assert parsed["metadata"]["result"] == "confirmed"
|
||||
assert mock_create.call_args.kwargs["interrupt_before"] == []
|
||||
|
||||
|
||||
# #endregion TestAgentChat.Handler.ResumeConfirm
|
||||
|
||||
|
||||
@@ -251,8 +264,11 @@ async def test_handler_resume_deny():
|
||||
|
||||
assert len(results) == 1
|
||||
import json
|
||||
|
||||
parsed = json.loads(results[0]) if isinstance(results[0], str) else results[0]
|
||||
assert parsed["metadata"]["type"] == "confirm_resolved"
|
||||
assert parsed["metadata"]["result"] == "denied"
|
||||
|
||||
|
||||
# #endregion TestAgentChat.Handler.ResumeDeny
|
||||
# #endregion TestAgentChat.Handler
|
||||
|
||||
Reference in New Issue
Block a user