feat(07-03): implement login CLI subcommand

- Add login_command function with QR code login, cookie import, and status check
- Add login subcommand with --import-cookie and --status options
- Update CLI help text with login examples
- Fix mock paths in tests to match actual import locations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 18:10:43 +08:00
parent 94c85baac9
commit d9861aa6c3
2 changed files with 109 additions and 18 deletions
+21 -18
View File
@@ -57,13 +57,14 @@ class TestLoginCommand:
self, mock_settings, mock_login_result_success
):
"""Test 1: login_default_mode - Default starts QR code login."""
with patch('kb_workflow.kb_workflow.get_settings', return_value=mock_settings), \
patch('kb_workflow.kb_workflow.CookieManager') as MockManager, \
patch('kb_workflow.kb_workflow.asyncio.run') as mock_run, \
patch('kb_workflow.kb_workflow.ZhihuLogin') as MockLogin:
with patch('kb_workflow.pipeline.config.get_settings', return_value=mock_settings), \
patch('kb_workflow.auth.cookie_manager.CookieManager') as MockManager, \
patch('kb_workflow.auth.ZhihuLogin') as MockLogin:
# Setup mock
mock_run.return_value = mock_login_result_success.model_dump()
mock_login_instance = MagicMock()
mock_login_instance.login_by_qrcode.return_value = mock_login_result_success
MockLogin.return_value = mock_login_instance
mock_manager = MagicMock()
MockManager.return_value = mock_manager
@@ -83,9 +84,9 @@ class TestLoginCommand:
{"name": "z_c0", "value": "imported_value", "domain": ".zhihu.com"}
]))
with patch('kb_workflow.kb_workflow.get_settings', return_value=mock_settings), \
patch('kb_workflow.kb_workflow.CookieManager') as MockManager, \
patch('kb_workflow.kb_workflow.parse_json_cookies') as mock_parse:
with patch('kb_workflow.pipeline.config.get_settings', return_value=mock_settings), \
patch('kb_workflow.auth.cookie_manager.CookieManager') as MockManager, \
patch('kb_workflow.auth.cookie_manager.parse_json_cookies') as mock_parse:
mock_parse.return_value = "z_c0=imported_value"
mock_manager = MagicMock()
@@ -101,7 +102,7 @@ class TestLoginCommand:
def test_login_status_no_cookie(self, mock_settings):
"""Test 3a: login_status - --status with no cookie returns invalid."""
with patch('kb_workflow.kb_workflow.get_settings', return_value=mock_settings):
with patch('kb_workflow.pipeline.config.get_settings', return_value=mock_settings):
# Act
result = login_command(status=True)
@@ -114,14 +115,14 @@ class TestLoginCommand:
self, mock_settings_with_cookie, mock_validation_result_valid
):
"""Test 3b: login_status - --status with valid cookie returns user info."""
with patch('kb_workflow.kb_workflow.get_settings', return_value=mock_settings_with_cookie), \
with patch('kb_workflow.pipeline.config.get_settings', return_value=mock_settings_with_cookie), \
patch('kb_workflow.kb_workflow.asyncio.run') as mock_run, \
patch('kb_workflow.kb_workflow.ZhihuLogin') as MockLogin:
patch('kb_workflow.auth.ZhihuLogin') as MockLogin:
# Setup mock
# Setup mock - asyncio.run returns the result object directly
mock_login_instance = MagicMock()
MockLogin.return_value = mock_login_instance
mock_run.return_value = mock_validation_result_valid.model_dump()
mock_run.return_value = mock_validation_result_valid # Return object, not dict
# Act
result = login_command(status=True)
@@ -135,12 +136,14 @@ class TestLoginCommand:
self, mock_settings, mock_login_result_success
):
"""Test 4: login_integration - Successful login saves cookie."""
with patch('kb_workflow.kb_workflow.get_settings', return_value=mock_settings), \
patch('kb_workflow.kb_workflow.CookieManager') as MockManager, \
patch('kb_workflow.kb_workflow.asyncio.run') as mock_run:
with patch('kb_workflow.pipeline.config.get_settings', return_value=mock_settings), \
patch('kb_workflow.auth.cookie_manager.CookieManager') as MockManager, \
patch('kb_workflow.auth.ZhihuLogin') as MockLogin:
# Setup mock
mock_run.return_value = mock_login_result_success.model_dump()
mock_login_instance = MagicMock()
mock_login_instance.login_by_qrcode.return_value = mock_login_result_success
MockLogin.return_value = mock_login_instance
mock_manager = MagicMock()
MockManager.return_value = mock_manager
@@ -155,7 +158,7 @@ class TestLoginCommand:
def test_login_error_handling(self, mock_settings):
"""Test 5: login handles exceptions gracefully."""
with patch('kb_workflow.kb_workflow.get_settings', side_effect=Exception("Config error")):
with patch('kb_workflow.pipeline.config.get_settings', side_effect=Exception("Config error")):
# Act
result = login_command()