JobData/tests/crawler_core/test_qcwy_sign.py
win 333a6d155e test(01-02): write sign algorithm unit tests for crawler_core
- Add tests/crawler_core/test_boss_sign.py: 13 tests for BossSign, _compute_checksum, _generate_uuid
- Add tests/crawler_core/test_qcwy_sign.py: 10 tests for Job51Sign and SIGN_KEY
- Add tests/crawler_core/test_zhilian_sign.py: 13 tests for ZhilianSign
- Add conftest.py at project root to add project root to sys.path
- Update pyproject.toml with [tool.pytest.ini_options] pythonpath config
- Fix crawler_core/__init__.py: wrap heavy-dep imports in try/except so sign subpackages are importable in lightweight envs without requests_go installed
- Remove tests/crawler_core/__init__.py to prevent namespace shadowing of crawler_core package
2026-03-21 18:20:43 +08:00

77 lines
2.8 KiB
Python

"""Unit tests for crawler_core.qcwy.sign — Job51Sign.
All tests are pure function assertions: no HTTP, no network, no mocks.
"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
import re
import pytest
from crawler_core.qcwy.sign import Job51Sign, SIGN_KEY
class TestJob51SignInit:
def test_default_sign_key(self):
signer = Job51Sign()
assert signer.sign_key == SIGN_KEY
assert len(SIGN_KEY) == 64 # 64-char hex key
def test_custom_sign_key(self):
custom_key = "a" * 64
signer = Job51Sign(sign_key=custom_key)
assert signer.sign_key == custom_key
class TestJob51SignBuildSignPath:
def setup_method(self):
self.signer = Job51Sign()
def test_returns_tuple_of_two_strings(self):
result = self.signer.build_sign_path("open/test")
assert isinstance(result, tuple)
assert len(result) == 2
assert all(isinstance(s, str) for s in result)
def test_get_path_format(self):
path, sign = self.signer.build_sign_path("open/test", "GET")
assert path.startswith("/open/test?api_key=51job&timestamp="), \
f"Path format wrong: {path}"
def test_sign_hex_length(self):
_, sign = self.signer.build_sign_path("open/test")
assert len(sign) == 64, f"Sign should be 64-char hex, got {len(sign)}: {sign}"
def test_sign_hex_format(self):
_, sign = self.signer.build_sign_path("open/test")
assert re.match(r'^[0-9a-f]{64}$', sign), f"Sign not hex: {sign}"
def test_get_vs_post_different_sign(self):
_, get_sign = self.signer.build_sign_path("open/test", "GET")
_, post_sign = self.signer.build_sign_path("open/test", "POST", body={"k": "v"})
assert get_sign != post_sign, "GET and POST should produce different signatures"
def test_get_with_params_includes_params_in_path(self):
path, _ = self.signer.build_sign_path("open/test", "GET", params={"city": "shanghai"})
assert "city" in path and "shanghai" in path, \
f"Params should appear in path: {path}"
def test_sign_key_in_path(self):
path, _ = self.signer.build_sign_path("open/jobs")
assert "api_key=51job" in path, f"api_key=51job missing from path: {path}"
class TestJob51SignGenerateUuid:
def test_generate_uuid_is_string(self):
uuid = Job51Sign.generate_uuid()
assert isinstance(uuid, str)
def test_generate_uuid_length(self):
uuid = Job51Sign.generate_uuid()
# 13-char ms timestamp + 10-char random int = 23 chars
assert len(uuid) == 23, f"Expected 23 chars, got {len(uuid)}: {uuid}"
def test_generate_uuid_numeric(self):
uuid = Job51Sign.generate_uuid()
assert uuid.isdigit(), f"UUID should be all digits: {uuid}"