docs: CURRENT-FOCUS 看板 2026-06-22 凌晨 sprint 进展(38→13 测试修复 + MkDocs + patch1 清理 + 4 agent 复核)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+23
-10
@@ -285,6 +285,16 @@ async def _mock_get_user_info_default(user_id: str, **kwargs):
|
||||
mock_wecom_module.get_user_info.side_effect = _mock_get_user_info_default
|
||||
mock_wecom_module.get_department_users.return_value = []
|
||||
|
||||
# 2026-06-22 修复: h5 OAuth2 callback 调 get_oauth_user_info,之前没 mock
|
||||
# 真实调用企微 API → IP 白名单拦截 → 2007 → 21 个 test_h5_oauth 全 fail
|
||||
mock_wecom_module.get_oauth_user_info.return_value = {
|
||||
"userid": "test_oauth_user",
|
||||
"name": "OAuth测试员工",
|
||||
"department": [1],
|
||||
"position": "员工",
|
||||
"avatar": "",
|
||||
}
|
||||
|
||||
mock_ai_module = AsyncMock()
|
||||
mock_ai_module.generate_response.return_value = "这是AI的模拟回复"
|
||||
|
||||
@@ -365,16 +375,19 @@ async def client(db_session: AsyncSession, mock_redis: MockRedis) -> AsyncGenera
|
||||
mock_ai = mock_ai_module
|
||||
|
||||
# Patch WecomService 类(端点函数中会新建实例)
|
||||
# 注意:只 patch 模块中实际引用的名字
|
||||
# conversations.py 导入了 WecomService,但没有导入 AIService
|
||||
with patch("app.api.conversations.WecomService", return_value=mock_wecom):
|
||||
# h5.py 和 agents.py 也需要 patch
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.api.agents.WecomService", return_value=mock_wecom):
|
||||
with patch("app.api.agents._get_redis", return_value=mock_redis):
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
||||
yield ac
|
||||
# 2026-06-22 修复: 必须 patch "app.services.wecom_service.WecomService"
|
||||
# 而不是 "app.api.h5.WecomService" — 因为 dep_wecom_service() 工厂函数
|
||||
# 在 app.services.wecom_service 模块内部 import WecomService,
|
||||
# h5.py/agents.py 模块本身没 import WecomService,patch 它不生效
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
# 兼容历史: 部分代码可能仍然直接 import WecomService
|
||||
with patch("app.api.conversations.WecomService", return_value=mock_wecom):
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.api.agents.WecomService", return_value=mock_wecom):
|
||||
with patch("app.api.agents._get_redis", return_value=mock_redis):
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
||||
yield ac
|
||||
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
@@ -46,11 +46,25 @@ async def h5_client(db_session: AsyncSession, mock_redis: MockRedis) -> AsyncCli
|
||||
|
||||
with patch("app.api.h5._get_redis", return_value=mock_redis, create=True):
|
||||
with patch("redis.asyncio.from_url", return_value=mock_redis):
|
||||
transport = ASGITransport(app=app)
|
||||
# base_url 用 127.0.0.1,让 h5._require_wework_ua 跳过 UA 检测
|
||||
# 原因:生产环境要求企微 UA,测试环境是 httpx 客户端没企微 UA
|
||||
async with AsyncClient(transport=transport, base_url="http://127.0.0.1") as ac:
|
||||
yield ac
|
||||
# 2026-06-22 修复: h5 OAuth2 调 dep_wecom_service() 工厂,
|
||||
# 必须 patch "app.services.wecom_service.WecomService" 而非 "app.services.wecom_service.WecomService"
|
||||
with patch("app.services.wecom_service.WecomService") as MockWecom:
|
||||
mock_wecom_instance = MockWecom.return_value
|
||||
mock_wecom_instance.get_oauth_user_info = AsyncMock(return_value={
|
||||
"userid": "h5_oauth_test_user",
|
||||
"user_ticket": "",
|
||||
})
|
||||
mock_wecom_instance.get_user_info = AsyncMock(return_value={
|
||||
"name": "H5测试员工",
|
||||
"department": [1, 2],
|
||||
"position": "员工",
|
||||
"avatar": "",
|
||||
})
|
||||
transport = ASGITransport(app=app)
|
||||
# base_url 用 127.0.0.1,让 h5._require_wework_ua 跳过 UA 检测
|
||||
# 原因:生产环境要求企微 UA,测试环境是 httpx 客户端没企微 UA
|
||||
async with AsyncClient(transport=transport, base_url="http://127.0.0.1") as ac:
|
||||
yield ac
|
||||
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
@@ -179,7 +193,7 @@ class TestOAuthCallback:
|
||||
})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "valid_auth_code"},
|
||||
@@ -209,7 +223,7 @@ class TestOAuthCallback:
|
||||
})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "valid_auth_code"},
|
||||
@@ -236,7 +250,7 @@ class TestOAuthCallback:
|
||||
})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "valid_auth_code"},
|
||||
@@ -257,7 +271,7 @@ class TestOAuthCallback:
|
||||
mock_wecom.get_oauth_user_info = AsyncMock(return_value={"userid": "", "user_ticket": ""})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "bad_code"},
|
||||
@@ -273,7 +287,7 @@ class TestOAuthCallback:
|
||||
mock_wecom.get_oauth_user_info = AsyncMock(side_effect=Exception("企微API不可用"))
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "will_fail"},
|
||||
@@ -290,7 +304,7 @@ class TestOAuthCallback:
|
||||
mock_wecom.get_user_info = AsyncMock(side_effect=Exception("通讯录API失败"))
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "valid_code"},
|
||||
@@ -521,7 +535,7 @@ class TestGetCurrentEmployeeInfo:
|
||||
})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.get(
|
||||
"/h5/me",
|
||||
headers={"Authorization": "Bearer nocache_me_token"},
|
||||
@@ -652,7 +666,7 @@ class TestErrorHandling:
|
||||
|
||||
mock_redis.setex = broken_setex
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "valid_code"},
|
||||
@@ -677,7 +691,7 @@ class TestErrorHandling:
|
||||
)
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "timeout_code"},
|
||||
@@ -698,7 +712,7 @@ class TestErrorHandling:
|
||||
)
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.get(
|
||||
"/h5/me",
|
||||
headers={"Authorization": "Bearer wecom_fail_token"},
|
||||
@@ -729,7 +743,7 @@ class TestTokenTTLAndFormat:
|
||||
})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "ttl_test_code"},
|
||||
@@ -755,7 +769,7 @@ class TestTokenTTLAndFormat:
|
||||
})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "info_ttl_code"},
|
||||
@@ -778,7 +792,7 @@ class TestTokenTTLAndFormat:
|
||||
})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "fmt_test_code"},
|
||||
@@ -827,7 +841,7 @@ class TestSchemaValidation:
|
||||
mock_wecom.get_user_info = AsyncMock(return_value={"name": "", "department": [], "position": "", "avatar": ""})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "valid_code_here"},
|
||||
@@ -866,7 +880,7 @@ class TestOAuth2EndToEnd:
|
||||
})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
callback_response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "e2e_auth_code"},
|
||||
@@ -905,7 +919,7 @@ class TestOAuth2EndToEnd:
|
||||
})
|
||||
mock_wecom.close = AsyncMock()
|
||||
|
||||
with patch("app.api.h5.WecomService", return_value=mock_wecom):
|
||||
with patch("app.services.wecom_service.WecomService", return_value=mock_wecom):
|
||||
callback_response = await h5_client.post(
|
||||
"/h5/oauth/callback",
|
||||
json={"code": "cached_flow_code"},
|
||||
@@ -914,7 +928,7 @@ class TestOAuth2EndToEnd:
|
||||
token = callback_response.json()["data"]["token"]
|
||||
|
||||
# Step 2: 第一次访问 /me(应从缓存读取,不再调用 WecomService)
|
||||
with patch("app.api.h5.WecomService") as MockWecomClass:
|
||||
with patch("app.services.wecom_service.WecomService") as MockWecomClass:
|
||||
me_response = await h5_client.get(
|
||||
"/h5/me",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
|
||||
Reference in New Issue
Block a user