""" 前程无忧 (51Job) HTTP 层 mock 测试(ARCH-04 / QUAL-03) 使用 unittest.mock.MagicMock 替代真实 HTTP 客户端,无网络依赖。 """ from __future__ import annotations from unittest.mock import MagicMock from spiderJobs.platforms.job51.api import ( GetCompanyInfo, GetJobDetail, SearchCompanyJobs, SearchRecommendJobs, _parse_job51_response, ) from spiderJobs.platforms.job51.client import Job51Client from crawler_core.base import Result # ───────────────────────────────────────────────────────── # 1. _parse_job51_response 纯函数测试 # ───────────────────────────────────────────────────────── class TestParseJob51Response: def test_http_error_returns_failure(self): result = _parse_job51_response(500, {}) assert result.success is False assert result.status_code == 500 def test_status_zero_returns_failure(self): result = _parse_job51_response(200, {"status": 0, "message": "系统繁忙"}) assert result.success is False assert "系统繁忙" in result.error def test_status_str_zero_returns_failure(self): result = _parse_job51_response(200, {"status": "0", "message": "错误"}) assert result.success is False def test_status_one_with_resultbody_joblist(self): raw = { "status": 1, "resultbody": { "jobList": {"items": [{"jobId": "123", "jobName": "Python 工程师"}], "totalCount": 1} } } result = _parse_job51_response(200, raw) assert result.success is True assert len(result.list) == 1 assert result.list[0]["jobName"] == "Python 工程师" def test_status_one_no_items_is_end_page(self): raw = {"status": 1, "resultbody": {"jobList": {"items": []}}} result = _parse_job51_response(200, raw) assert result.success is True assert result.is_end_page is True def test_non_dict_raw_returns_failure(self): result = _parse_job51_response(200, "not a dict") assert result.success is False def test_detail_payload(self): raw = {"status": 1, "resultbody": {"companyName": "测试公司"}} result = _parse_job51_response(200, raw) assert result.success is True assert result.data["companyName"] == "测试公司" # ───────────────────────────────────────────────────────── # 2. SearchRecommendJobs # ───────────────────────────────────────────────────────── class TestSearchRecommendJobs: def _make_client(self, return_value): mock_client = MagicMock() mock_client.post.return_value = return_value return mock_client def test_search_success(self): raw = { "status": 1, "resultbody": { "jobList": {"items": [{"jobId": "1", "jobName": "测试职位"}]} } } searcher = SearchRecommendJobs(job_area="020000", client=self._make_client((200, raw))) result = searcher.search(page_index=1) assert result.success is True assert len(result.list) == 1 def test_search_http_error(self): searcher = SearchRecommendJobs(client=self._make_client((403, {}))) result = searcher.search(page_index=1) assert result.success is False assert result.status_code == 403 def test_search_biz_error(self): raw = {"status": 0, "message": "接口限流"} searcher = SearchRecommendJobs(client=self._make_client((200, raw))) result = searcher.search(page_index=1) assert result.success is False # ───────────────────────────────────────────────────────── # 3. GetJobDetail(路径拼接版) # ───────────────────────────────────────────────────────── class TestGetJobDetail: def test_fetch_success(self): mock_client = MagicMock() mock_client.get.return_value = (200, { "status": 1, "resultbody": {"jobName": "数据工程师", "salary": "20k-30k"}, }) fetcher = GetJobDetail(job_id="170651439", client=mock_client) result = fetcher.fetch() assert result.success is True assert result.data["jobName"] == "数据工程师" def test_fetch_exception_handled(self): mock_client = MagicMock() mock_client.get.side_effect = ConnectionError("网络超时") fetcher = GetJobDetail(job_id="123", client=mock_client) result = fetcher.fetch() assert result.success is False assert "网络超时" in result.error def test_fetch_http_error(self): mock_client = MagicMock() mock_client.get.return_value = (404, {}) fetcher = GetJobDetail(job_id="nonexist", client=mock_client) result = fetcher.fetch() assert result.success is False assert result.status_code == 404 # ───────────────────────────────────────────────────────── # 4. GetCompanyInfo # ───────────────────────────────────────────────────────── class TestGetCompanyInfo: def test_fetch_success(self): mock_client = MagicMock() mock_client.get.return_value = (200, { "status": 1, "resultbody": {"companyName": "测试科技有限公司", "coId": "9825088"}, }) fetcher = GetCompanyInfo(company_id="9825088", client=mock_client) result = fetcher.fetch() assert result.success is True assert result.data["companyName"] == "测试科技有限公司" def test_fetch_exception(self): mock_client = MagicMock() mock_client.get.side_effect = TimeoutError("请求超时") fetcher = GetCompanyInfo(company_id="123", client=mock_client) result = fetcher.fetch() assert result.success is False # ───────────────────────────────────────────────────────── # 5. SearchCompanyJobs # ───────────────────────────────────────────────────────── class TestSearchCompanyJobs: def test_search_success(self): mock_client = MagicMock() mock_client.post.return_value = (200, { "status": 1, "resultbody": {"items": [{"jobId": "1"}], "totalCount": 1}, }) searcher = SearchCompanyJobs(company_id="9825088", client=mock_client) result = searcher.search(page_index=1) assert result.success is True assert len(result.list) == 1 def test_search_empty(self): mock_client = MagicMock() mock_client.post.return_value = (200, { "status": 1, "resultbody": {"items": [], "totalCount": 0}, }) searcher = SearchCompanyJobs(company_id="9825088", client=mock_client) result = searcher.search(page_index=1) assert result.success is True assert result.is_end_page is True # ───────────────────────────────────────────────────────── # 6. Job51Client — sign 注入 # ───────────────────────────────────────────────────────── class TestJob51ClientHeaders: def test_headers_contain_sign(self): client = Job51Client() headers = client._job51_headers(sign="test_sign_value") assert headers["sign"] == "test_sign_value" def test_headers_uuid_format(self): client = Job51Client() headers = client._job51_headers(sign="abc") assert len(headers["uuid"]) >= 20 def test_headers_empty_account(self): client = Job51Client() headers = client._job51_headers(sign="xyz") assert headers["user-token"] == "" assert headers["account-id"] == ""